SlideShare una empresa de Scribd logo
Enciclopedia del Lenguaje
""."" ADDISON-WESLEY IBEROAMERICANA
Enciclopedia del Lenguaje
c
Enciclopedia del Lenguaje
cFeo. Javier Ceballos Sierra
Profesor titular de la
Escuela Universitaria Politecnica
Universidad de Alcala de Henares (Madrid)
TAT ADDISON-WESLEY IBEROAMERICANA
Se ha puesto el maximo empefio en ofrecer allector una informacion
completa y precisa. Sin embargo RA-MAEditorial y Addison-Wesley
Iberoamericana, S.A.no asumen ninguna responsabilidad derivada de
su usa, ni tam poco por cualquier violacion de patentes ni otros
derechos de terceras partes que pudieran ocurrir.
© 1993 por Addison Wesley Iberoamericana, S.A.
Wilmington, Delaware, E.U.A.
Ninguna parte de este libro puede ser reproducida, grabada en sistema
de almacenamiento 0 transmitida en forma alguna ni por cualquier
procedimiento, ya sea electronico, mecanico, reprografico, magnetico 0
cualquier otro, sin autorizacion previa y por escrito de RA-MA.
Dedico esta obra
a Marfa del Carmen, mi esposa,
y a mis hijos Francisco y' Javier
CAPITULO 1. Introduccion al Lenguaje C. . . . . . . . . . . . . . . 39
Historia del lenguaje C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Realizaci6n de un programa en C. . . . . . . . . . . . . . . . . . . . . 41
Edici6n de un programa. . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Salvar el programa escrito, en el disco. . . . . . . . . . . . . . . 44
Compilar y ejecutar el programa. . . . . . . . . . . . . . . . . . . . 44
Salvar el programa ejecutable (.exe), en el disco. . . . . . . 44
Depurar un programa............................. 45
Preparando un programa simple. . . . . . . . . . . . . . . . . . . . 45
Edici6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Compilaci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Depuraci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Nombres de ficheros y extensiones. . . . . . . . . . . . . . . . . . . . . 49
CAPITULO 2. Elementos del Lenguaje C........ .... .... 51
Presentaci6n de la sintaxis de C. . . . . . . . . . . . . . . . . . . . . . . 51
Caracteres de C. .. .. . . . . . . . . . . . . . .. . .. . .. .. .. . . . . . . . 52
Letras, digitos y carcicter de subrayado. . . . . . . . . . . . . . . 52
Espacios en blanco. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Caracteres especiales y signos de puntuaci6n. . . . . . . . . 53
Secuencias de escape.............................. 53
Tipos de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Tipos fundamentales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
char. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
short. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
long. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
enum. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Creaci6n de una enumeraci6n. . . . . . . . . . . . . . . . . . . . . . 58
float. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
double. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
long double...................................... 61
void. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Tipos derivados..................................... 61
punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
uni6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Nombres de tipos................................... 63
typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Constantes enteras................................ 65
Constantes reales................................. 66
.. ". Constante de un solo carcicter. . . . . . . . . . . . . . . . . . . . . . 67
.Constantes de caracteres........................... 67
'Identificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Palabras clave. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Declaraci6n de constantes............................ 72
Cailficador const .. " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Calificador volatile.... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Expresiones numeric'as............................... 73
Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Operadores aritmeticos............................ 74
Operadores 16gicos................................ 74
Operadores de relaci6n............................ 75
Expresiones de Boole. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Operadores unitarios.............................. 76
Operadores 16gicos para manejo de bits. . . . . . . . . . . . . . 76
Operadores de asignaci6n.......................... 77
Expresiones condicionales.......................... 79
Otros operadores.................................... 79
Operador coma................................... 79
Operador de indirecci6n (*)........................ 80
Operador de direcci6n-de (&). . . . . . . . . . . . . . . . . . . . . . . 80
Operador sizeof (tamafio de).. . . . . . . . . . . . . . . . . . . . . . 80
Priori dad y orden de evaluaci6n. . . . . . . . . . . . . . . . . . . . . . . 81
Conversi6n de tipos................................. 82
Conversi6n explicita del tipo de una expresi6n. . . . . . . . . . 85
Tipos estandar...................................... 86
CAPITUW 3. Comenzando con el Lenguaje C. . . . . . . . . . . 89
Estructura de un programa C. . . . . . . . . . . . . . . . . . . . . . . . . 89
Ficheros de cabecera. Directriz # include. . . . . . . . . . . . . 91
Directriz # define. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Declaraciones y definiciones. . . . . . . . . . . . . . . . . . . . . . . . 92
Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Sentencias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Sentencia compuesta 0 bloque. . . . . . . . . . . . . . . . . . . . . . 94
Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Declaraci6n de una funci6n. . . . . . . . . . . . . . . . . . . . . . . . 95
Definici6n de una funci6n......................... 96
Llamada a una funci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Pasando argumentos a funciones.................... 98
Un programa C formado por multiples ficheros. . . . . . . . . 99
Accesibilidad de variables. Ambito.................... 101
Variables glob ales y locales. . . . . . . . . . . . . . . . . . . . . . . . . 101
Clases de almacenamiento. . . . . . . . . . . . . . . . . . . . . . . . . . 103
Variables declaradas a nivel externo. . . . . . . . . . . . . . . . . 104
Variables declaradas a nivel interno. . . . . . . . . . . . . . . . . . 106
Declaraci6n de funciones a nivel interno y a nivel externo 108
Sintaxis de las sentencias y funciones de C. . . . . . . . . . . . . 108
Senten cia de asignaci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Entrada y salida estandar............................ 110
Salida con formato. Funci6n printf. . . . . . . . . . . . . . . . . . . . 110
Entrada con formato. Funci6n scanf. . . . . . . . . . . . . . . . . . . 116
Entrada de caracteres. getchar........................ 122
Salida de caracteres. putchar. . . . . . . . . . ... . . . . . . . . . . . . . . 122
Caracter fin de linea y caracter fin de fichero. . . . . . . . . . . 123
Funciones getch y getche. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Funci6n system..................................... 126
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
CAPITUW 4. Sentencias de Control.................... 129
Senten cia if. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Anidamiento de sentencias if.. . . . . . . . . . . . . . . . . . . . . . . . 131
Estructura if. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Sentencia switch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Sentencia break..................................... 138
Sentencia while..................................... 140
Sentencia do. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Senten cia for....................................... 145
Bucles anidados..................................... 146
Sentencia continue.................................. 149
Sentencia goto y etiquetas. . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Numeros pseudoaleatorios............................ 158
Calculo de areas y volumenes. . . . . . . . . . . . . . . . . . . . . . 161
CAPITUW 5. Tipos Estructuradosde Datos............. 163
Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Declaraci6n de un array. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
Arrays unidimensionales........................... 164
Arrays multidimensionales......................... 165
Caracteristicas generales........................... 166
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Cadenas de caracteres. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Funci6n gets. Leer una cadena de caracteres. . . . . . . . . . . . 175
Funci6n puts. Escribir una cadena de caracteres. . . . . . . . . 175
Limpiar el buffer asociado con stdin. . . . . . . . . . . . . . . . . . . 176
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
Arrays de cadenas de caracteres. . . . . . . . . . . . . . . . . . . . . . . 182
Funciones para manipular cadenas de caracteres. . . . . . . . . 183
Funciones para conversi6n de datos. . . . . . . . . . . . . . . . . . . . 190
Funciones para clasificaci6n y conversi6n de caracteres. . . 193
Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Creaci6n de una estructura. . . . . . . . . . . . . . . . . . . . . . . . . 197
Operaciones con estructuras. . . . . . . . . . . . . . . . . . . . . . . . 200
Arrays de estructuras.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Union~s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
Campos de bits .. ; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
CAPITUW 6. Punteros.... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Creaci6n de punteros................................ 219
Operadores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Importancia del tipo del objeto al que se apunta. . . . . . 221
Operaciones con punteros............................ 222
Operaci6n de asignaci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Operaciones aritmeticas............................ 222
Comparaci6n de punteros. . . . . . . . . . . . . . . . . . . . . . . . . . 223
Ejemplos con punteros............................ 223
Punteros a objetos de tipo no especificado (void). . . . . 225
Punteros y arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Punteros a cadenas de caracteres.. . . . . 227
Inicializaci6n de cadenas. . . . . . . . . . . . . . . . . . . . . . . . . . . 232
Arrays de punteros. Punteros a punteros. . . . . . . . . . . . . . . 233
Inicializaci6n de un array de punteros a cadenas de
caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Asignaci6n dimimica de memoria. .. ... . . . . . ... . . .. . . . 239
Funciones para asignaci6n dinamica de memoria. . . . . . . . 240
malloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
Arrays dinamicos................................. 242
calloc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
realloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
free. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
halloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
hfree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
Punteros a estructuras............................... 247
Declaraciones complejas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
CAPITUW 7. Funciones............................... 251
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
Definici6n de una funci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Cuerpo de la funci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
Valor retornado por una funci6n. Sentencia return. . . . 255
Llamada a una fund6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
Declaraci6n de una' funci6n. (Funci6n prototipo). . . . . . . . 257
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Pasando parametros por valor 0 por referencia. . . . . . . . . . 267
Pasando arrays completos. . . . . . . . . . . . . . . . . . . . . . . . . . 268
Pasando punteros................................. 272
Argumentos en la linea de 6rdenes. . . . . . . . . . . . . . . . . . . . 274
Funciones con un numero de argumentos variable. . . . . . . 276
Funciones recursivas................................. 279
Ajustando e1tamafio del stack. . . . . . . . . . . . . . . . . . . . . . 281
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Punteros a funciones................................ 288
Funciones predefinidas en C. . . . . . . . . . . . . . . . . . . . . . . . . . 292
Funciones matematicas............................ 292
Otras funciones de interes. . . . . . . . . . . . . . . . . . . . . . . . . . 299
Funci6n C para clasificar datos. . . . . . . . . . . . . . . . . . . . . . . 303
qsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
Funciones C para busqueda.......................... 305
bsearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
CAPITULO 8. Funciones ESbindar de E/S................ 315
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
Manipulaci6n de ficheros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Abriendo un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Cerrando un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
Leyendo y escribiendo datos. . . . . . . . . . . . . . . . . . . . . . . . 318
Detecci6n de errores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
Acceso secuencial y acceso aleatorio. . . . . . . . . . . . . . . . . 319
Abrir un fichero.................................... 319
fopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
fdopen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
freopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
Cerrar un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
fclose. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
fcloseall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Detecci6n de errores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
ferror. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
clearerr. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
feof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
perror . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Entrada/salida canicter a caracter. . . . . . . . . . . . . . . . . . . . . 326
fputc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
fgetc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
Entrada/salida palabra a palabra. . . . . . . . . . . . . . . . . . . . . . 330
putw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
getw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
Entrada/salida de cadenas de caracteres. . . . . . . . . . . . . . . . 332
fputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
fgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
Utilizaci6n de dispositivos estandar. . . . . . . . . . . . . . . . . . . . 334
Entrada/salida con formato.......................... 336
fprintf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
fscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
Entrada/salida utilizando registros 0 bloques. . . . . . . . . . . 338
fwrite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
fread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
Control de la memoria intermedia asociada con un
fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
setbuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
setvbuf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
fflush . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
Ficheros temporales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
tmpfile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
rmtmp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
tmpnam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
tempnam .. . . . . . . . . . . . . . . . 347
Acceso aleatorio a un fichero. . . . . . . . . . . . . . . . . . . . . . . . . 348
fseek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
ftell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
rewind. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
fsetpos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
fgetpos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
_fsopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
CAPITUW 9. FUDcioDes de E/S de bajo Dive'. . . . . . . . . . . . 355
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
Manipulaci6n de ficheros , . 355
Abriendo un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
Cerrando un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
Leyendo y escribiendo datos. . . . . . . . . . . . . . . . . . . . . . . . 357
Abrir un fichero.................................... 357
open. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
creat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
eof. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
fileno. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Cerrar un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
close. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Funciones para entrada/salida........................ 363
write. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
read. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Acceso aleatorio.................................... 366
lseek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
tell. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
dup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
dup2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
sopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
umask. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
CAPITUW 10. Funciones para la Consola y los Puertos de E/S 373
Introducci6n ". . . . . . . . . . . . . . . . . . . . 373
Funciones para la consola. . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
getch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
getche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
putch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
kbhit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
ungetch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
cgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
cputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
cscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
cprintf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
Control del cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
Funciones para los puertos de E/S. . . . . . . . . . . . . . . . . . . . 384
inp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
outp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
inpw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
outpw , . . . . . . . . . . . . . . . . . . 386
CAPITUW 11. EI Preprocesador de C. . . . . . . . . . . . . . . . . . . 391
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
Directriz # define. Sustituci6n de simbolos. . . . . . . . . . . . . . 392
El operador #.................................... 394
El operador # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
Directriz # undef. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
Directriz # include. Inclusi6n de ficheros fuente. . . . . . . . . 395
Compilaci6n condicional............................. 396
defined(identificador) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
Directrices # ifdef e # ifndef. . . . . . . . . . . . . . . . . . . . . . . . . . 399
Directriz # line. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
Directriz # error. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
Directriz # pragma. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
Funciones intrinsecas................................ 401
Utilizando ficheros de cabecera (.h). . . . . . . . . . . . . . . . . . . . 403
Utilizando el preprocesador. . . . . . . . . . . . . . . . . . . . . . . . . . . 405
Utilizando funciones intrinsecas. . . . . . . . . . . . . . . . . . . . . 407
Utilizando macros 0 funciones. . . . . . . . . . . . . . . . . . . . . . 408
CAPITUW 12. Estructuras Dimimicasde Datos.......... 413
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
Asignaci6n dimimica de memoria..................... 414
Listas lineales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
Operaciones basicas................................. 418
Inserci6n de un elemento al comienzo de la lista. . . . . . 418
Inserci6n de un elemento en general. . . . . . . . . . . . . . . . . 419
Borrar un elemento de la lista. . . . . . . . . . . . . . . . . . . . . . 420
Recorrido de una lista cuyo primer elemento esta apunta-
do por p. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
Buscar en una lista un elemento con un valor x. . . . . . . 422
Pilas, colas y listas doblemente enlazadas. . . . . . . . . . . . . . . 427
Pilas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
Colas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
Listas circulares. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
Listas doblemente enlazadas. . . . . . . . . . . . . . . . . . . . . . . . 444
Arboles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
Arboles binarios.................................. 450
Recorrido de arboles'binarios............... . . . . .... 451
Arboles binarios de busqueda. . . . . . . . . . . . . . . . . . . . . . . . . 453
Borrado en arboles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
Arboles binarios perfectamente equilibrados. . . . . . . . . . . . 460
CAPITUW 13. Algoritmos Recursivos, de Ordenacion y de
Btisqueda. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
Recursividad. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
Clasificaci6n de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
Metodo de la burbuja...... . . . . . . . . . . . . . . . . . . . . . . . 473
Metodo de inserci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
Metodo Quicksort................................ 480
Comparaci6n de los metodos expuestos. . . . . . . . . . . . . . 484
Bdsqueda de datos. 485
Bdsque4a secuencial. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485
Bdsqueda binaria................................. 486
Ordenaci6n de ficheros en disco. . . . . . . . . . . . . . . . . . . . . . . 488
Ordenaci6n de ficheros. Acceso secuencial........... 488
Ordenaci6n de ficheros. Acceso aleatorio............ 494
Algoritmos hash.................................... 497
Arrays hash...................................... 498
Metodo hash abierto.............................. 499
Metodo hash con overflow. . . . . . . . . . . . . . . . . . . . . . . . . 501
Eliminaci6n de elementos.......................... 502
Un ejemplo de un array hash. . . . . . . . . . . . . . . . . . . . . . . 503
CAPITUW 14. Manejo de la Memoria. . . . . . . . . . . . . . . . . . 509
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
Tamano de una variable tipo puntero. . . . . . . . . . . . . . . . . . 510
Punteros y segmentos de 64K. . . . . . . . . . . . . . . . . . . . . . . . . 510
Punteros near. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
Punteros far........................................ 511
Punteros huge...................................... 514
Punteros basados en un segmento. . . . . . . . . . . . . . . . . . . . . 514
Modelos de memoria estandar. . . . . . . . . . . . . . . . . . . . . . . . 515
Modelo pequenito (tiny)........................... 516
Modelo pequeno (small)........................... 517
Modelo medio (medium). . . . . . . . . . . . . . . . . . . . . . . . . . . 518
Modelo compacta (compact)....................... 519
Modelo grande (large)............................. 520
Modelo enorme (huge)............................ 523
Punteros nul os...................................... 524
Modelos de memoria mixtos. . . . . . . . . . . . . . . . . . . . . . . . . . 526
Declaraci6n de variables near, far, huge, 0 based. . . . . . . . 529
Declaraci6n de funciones far 0 near. . . . . . . . . . . . . . . . . . . 530
Utilizaci6n de punteros basad os en un segmento. . . . . . . . 530
Variables y punteros basados en un segmento constante 532
Punteros basados en un segmento variable. . . . . . . . . . . 533
Punteros basados sobre un puntero. . . . . . . . . . . . . . . . . . 536
Punteros basados en void. . . . . . . . . . . . . . . . . . . . . . . . . . 538
Punteros basados en su propio segmento. . . . . . . . . . . . . 539
Soporte MS-DOS para asignaci6n de memoria. . . . . . . . . . 540
Soporte MS-DOS para cadenas de caracteres. . . . . . . . . . . . 542
Manipulaci6n de bloques de memoria. . . . . . . . . . . . . . . . . . 542
Soporte MS-DOS para manipulaci6n de bloques de
memoria. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
CAPITULO 15. Compilar y Enlazar desde DOS. . . . . . . . . . . 549
Introducci6n................................. 549
Proceso para crear un fichero ejecutable. . . . . . . . . . . . . 550
Orden CL.......................................... 550
Extensiones de ficheros............................ 553
Opciones de CL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
Generaci6n de c6digo............................. 554
Operaciones en coma flotante. . . . . . . . . . . . . . . . . . . . . . 555
Lenguaje. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
Enlace. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
Modelos de memoria. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
Optimizaci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
Ficheros de salida. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
Preprocesador. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
Listado de ficheros fuente. . . . . . . . . . . . . . . . . . . . . . . . . . 559
Opciones varias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
Orden LINK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560
LINK en modo pregunta/respuesta. . . . . . . . . . . . . . . . . . 565
LINK con respuestas automatic as . . . . . . . . . . . . . . . . . . . 566
Overlays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567
Orden ILINK....................................... 568
CAPITULO 16. Librerias y Utilidades del Compilador. . . . . 571
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571
Orden LIB......................................... 572
LIB en modo pregunta/respuesta................... 575
LIB con respuestas automaticas.. . .. . . . .. .. 576
Un ejemplo de trabajo con librerias. . . . . . . . . . . . . . . . . 577
Orden NMAKE..................................... 579
EI fichero makefile. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
Opciones de NMAKE............................. 582
Macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
Sustituciones en macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
Macros especiales................................. 584
Caracteres que pueden modificar estas macros. . . . . . . . 585
Reglas de inferencia............................... 586
Directrices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
Prioridades. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
Ficheros en linea. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590
Simbolos especiales............................... 591
Componentes de una descripci6n de fichero. . . . . . . . . . 591
Pseudoobjetivos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592
NMAKE con respuestas automaticas................ 592
Inicializaci6n automatica. TOOLS.INI............... 593
Un ejemplo de trabajo con la utili dad NMAKE. . . . . . . 593
EI depurador Code View de Microsoft. . . . . . . . . . . . . . . . . 596
Com pilar y enlazar un programa C para depurar. . . . . 597
Invocando a Code View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597
Opciones de Code View........................... 598
Menus de Code View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601
Code View con rat6n (mouse). . . . . . . . . . . . . . . . . . . . . . . . 603
Seleccionando texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604
Menu File.......................................... 604
Menu Edit......................................... 605
Menu View......................................... 606
Menu Search....................................... 609
Menu Run. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611
Menu Watch........................................ 612
Menu Options...................................... 614
Menu Calls......................................... 616
Otras utilidades suministradas con Microsoft C. . . . . . . . . 616
CVPACK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
HELPMAKE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
BIND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
QH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
EXEHDR. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
EXP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618
RM .. . . . . . . . .. . . . . . . . . . . . . . . . 618
UNDEL......................................... 618
CAPITUW 17. Rutinas en Lenguaje Ensamblador. . . . . . . . 619
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
Rutinas en lenguaje ensamblador en linea con sentencias C 620
El lenguaje ensamblador en bloques _asm. . . . . . . . . . . . . 622
COnstantes enteras................................ 623
Definici6n de datos............................... 623
Operadores y expresiones.......................... 623
Macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
Pseudoinstrucci6n _emit.......................... 625
Utilizando elementos de C en un bloque _asm. . . . . . . . . 626
Definici6n de macros en lenguaje ensamblador. . . . . . . . . . 629
Utilizando y salvando los registros. . . . . . . . . . . . . . . . . . . . . 630
Formas de utilizar un bloque _asm. . . . . . . . . . . . . . . . . . . 633
Llamando a funciones C. . . . . . . . . . . . . . . . . . . . . . . . . . . 633
Reemplazar una funci6n C. . . . . . . . . . . . . . . . . . . . . . . . . 634
Manipulaci6n de interrupciones. . . . . . . . . . . . . . . . . . . . . 635
Trabajando con punteros. . . . . . . . . . . . . . . . . . . . . . . . . . . 635
Trabajando con estructuras. . . . . . . . . . . . . . . . . . . . . . . . . 637
Salto a una etiqueta. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
Depuraci6n y optimizaci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . 641
M6dulos separados en lenguaje ensamblador. . . . . . . . . . . . 641
Entrando al procedimiento. . . . . . . . . . . . . . . . . . . . . . . . . 642
Salvar el valor de los registros. . . . . . . . . . . . . . . . . . . . . . 642
Acceso a los parametros de la pila. . . . . . . . . . . . . . . . . . 643
Devolver un valor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
Llamando a un procedimiento en ensamblador des de C. . 644
CAPITUW 18. Comunicaciones. Servicios del DOS y del BIOS 649
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
Funciones para Hamar al DOS. . . . . . . . . . . . . . . . . . . . . . . . 650
int86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650
int86x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
intdos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
intdosx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
segread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
bdos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
FP _OFF. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
FP_SEG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
Un ejemplo de comunicaciones. . . . . . . . . . . . . . . . . . . . . 654
Un ejemplo de llamadas al DOS. . . . . . . . . . . . . . . . . . . . 656
Servicios del BIOS.................................. 658
Servicios del disco. _bios_disk. . . . . . . . . . . . . . . . . . . . 658
Listado del equipo. _bios_equiplist. . . . . . . . . . . . . . . . 660
Servicios del teclado. _bios_keybrd. . . . . . . . . . . . . . . . 660
Tamafio de la memoria. _bios-lIlemsize. . . . . . . . . . . . 661
Servicios del puerto paralelo. _bios_printer. . . . . . . . . 662
Servicios del puerto serie. _bios_serialcom. . . . . . . . . . 664
Panimetros de inicializaci6n del puerto. . . . . . . . . . . . . . 664
Servicios del reloj. _bios_timeofday............... 666
CAPITUW 19. C y DOS........ . . . . . . . . . . . . . . . . . . . . . . 669
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Directorios y caminos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Definiciones generales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Camino (path). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Redirecci6n de la salida. . . . . . . . . . . . . . . . . . . . . . . . . . . . 671
Redirecci6n de la entrada. . . . . . . . . . . . . . . . . . . . . . . . . . 671
Interconexi6n de entradas y salidas estandar. . . . . . . . . . 672
Prompt. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
Operaciones con directorios. . . . . . . . . . . . . . . . . . . . . . . . . . . 673
Especificaci6n de un path. . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
Funciones para control de directorios. . . . . . . . . . . . . . . . . . 674
chdir. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
mkdir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675
rmdir. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675
getcwd. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
system. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
Funciones para manipulaci6n de ficheros. . . . . . . . . . . . . . . 679
access. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
chmod. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
chsize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
unlink. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
rename. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
setmode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
stat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
isatty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685
utime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685
Utilizando c6digos de salida. . . . . . . . . . . . . . . . . . . . . . . . . . 686
Soporte DOS para llamadas al sistema. . . . . . . . . . . . . . . . . 687
Programas residentes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692
CAPITUW 20. Control de Procesos. . . . . . . . . . . . . . . . . . . . . 697
Introducci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
Iniciaci6n de un proceso..... . . . . . . . . . . . . . . . . . . . . . . . . 698
Ejecuci6n de un proceso.... . . . . . . . . . . . . . . . . . . . . . . . . . 699
Terminaci6n de un proceso. . . . . . . . . . . . . . . . . . . . . . . . . . . 700
Funciones para control de procesos. . . . . . . . . . . . . . . . . . . . 701
Terminaci6n de procesos............................. 701
abort. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . 701
exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702
Ejecuci6n de procesos............................... 702
atexit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702
onexit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
setjmp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
longjmp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
_fpreset. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
signal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
raise. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
Comenzar un nuevo proceso. . . . . . . . . . . . . . . . . . . . . . . . . . 712
execxxx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
spawnxxx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
getenv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
putenv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
CAPITUW 21. Gnificos con C. . . . . . . . . . . . . . . . . . . . . . . . . 725
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
Estructura de un programa gnifico........... 726
Modalidades de video disponibles........... . . . . . . . . . . 728
Seleccionar la modalidad de video. . . . . . . . . . . . . . . . . . 729
Restaurar la modalidad de video original. . . . . . . . . . . . . 730
Almacenar caracteres. Funci6n sprintf. . . . . . . . . . . . . . . 730
Estructura para almacenar la configuraci6n de video. . 731
Colores en modo texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734
Colores en modo gnifico utilizando CGA. . . . . . . . . . . . . . 736
Colores en modo gnifico utilizando VGA, MCGA y EGA 739
Especificaci6n de coordenadas. . . . . . . . . . . . . . . . . . . . . . . . 740
CO'ordenadas fisicas............................... 740
Coordenadas 16gicas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740
Volviendo a coordenadas fisicas. . . . 741
Convertir coordenadas fisicas a 16gicas y viceversa. . . . 741
Funciones gnificas .... ; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742
Funciones relativas a configuraci6n. . . . . . . . . . . . . . . . . . . . 742
Funciones relativas a coordenadas. . . . . . . . . . . . . . . . . . . . . 745
Funciones referentes al uso de paletas. . . . . . . . . . . . . . . . . . 748
Funciones para obtener 0 poner atributos. . . . . . . . . . . . . . 749
Creaci6n de una mascara. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
Visualizar imagenes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 754
Visualizar texto..................................... 761
Animaci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764
Animaci6n de un objeto............................. 766
Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771
Coordenadas reales en una ventana. . . . . . . . . . . . . . . . . . . . 775
Funciones para un sistema de coordenadas cartesianas. . . 779
CAPITUW 22. Presentaciones Gnificas. . . . . . . . . . . . . . . . . . 785
Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785
Estructura de un programa para presentaciones graficas. . 785
Funciones para presentaciones graficas. . . . . . . . . . . . . . . . . 787
Tipos de letras (fonts)............................... 796
Funciones para representar distintos tipos de letras. . . . . . 797
CAPITUW 23. Utilizaci6n del PWB.................... 805
PWB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
Menus de PWB. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
El menu principal....... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808
PWB con rat6n (mouse)............................. 809
Ventanas de dialogo................................. 810
El menu File....................................... 811
Caracteristicas del editor del PWB... . . . . . 814
Seleccionando texto............................... 815
Operaciones con el editor............................ 816
Mover el cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
Scroll. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
Insertar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
Borrar '. . . . . . . . . . . . . . . . 817
Seleccionar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
Copiar, mover 0 borrar el texto seleccionado. . . . . . . . . 817
Buscar y sustitu~..... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818
Teclas de funci6n................................. 818
Menu Edit......................................... 818
Moviendo y copiando texto. . . . . . . . . . . . . . . . . . . . . . . . . . . 821
Menu View......................................... 822
Menu Search....................................... 824
Copiar texto de otros ficheros. . . . . . . . . . . . . . . . . . . . . . . . . 829
Programas y m6dulos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830
Menu Make........................................ 831
Menu Run.. . . . . . . . .. . . . . . . . . 835
Menu Options...................................... 836
Menu Browse....................................... 842
Menu Help......................................... 844
CAPITUW 24. Instalaci6n de Microsoft C. . . . . . . . . . . . . . . 847
Sistema Requerido. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847
Caracteristicas aportadas a partir de la versi6n 6. . . . . . . . 848
Instalaci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 848
Ejecuci6n del programa SETUP. . . . . . . . . . . . . . . . . . . . . . . 849
APENDICE A. Ficheros .h de C........................ 857
Ficheros de cabecera, variables globales y tipos 857
APENDICE B. C6digos de caracteres (ASCII). . . . . . . . . . . . 869
C6digos extendidos.................................. 875
C6digos del teclado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
Saludo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Cuadrados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Paso de grados Centigrados a Fahrenheit (F=9/5*C+32). . . . . 90
Funci6n intercambio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Valor mayor de tres valores dados. . . . . . . . . . . . . . . . . . . . . . . . .. 100
Ambito de las variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 102
Declaraci6n de extern a nivel externo. . . . . . . . . . . . . . . . . . . . . .. 104
Declaraciones a nivel interno. . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 107
Leyendo datos de la entrada estandar. . . . . . . . . . . . . . . . . . . . . .. 123
Capital e Intereses....................................... 127
Soluci6n de una ecuaci6n de segundo grado. . . . . . . . . . . . . . . .. 128
Menor de tres numeros a, bye. . . . . . . . . . . . . . . . . . . . . . . . . . .. 133
Cantidad a pagar en funci6n de la cantidad comprada. . . . . . .. 134
Dias correspondientes a un mes de un ano dado. . . . . . . . . . . .. 137
Importe por vehkulo al circular por una autopista. . . . . . . . . .. 138
Simulaci6n de una maquina sumadora. . . . . . . . . . . . . . . . . . . . .. 141
C6digo ASCII de un caracter. . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 141
Cuadrados que se pueden expresar como suma de otros dos .. 142
Raiz cuadrada de un numero. Metodo de Newton. . . . . . . . . . .. 143
Construir un triangulo de n filas con caracteres. . . . . . . . . . . . .. 147
Tablero de Ajedrez. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 148
Areas de circulos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 150
goto salir............................................... 151
Calcular las rakes de una ecuaci6n de 2? grado. . . . . . . . . . . .. 152
Palabras con cuatro 0 mas vocales diferentes. . . . . . . . . . . . . . .. 154
Contar caracteres, palabras y lineas en un texto. . . . . . . . . . . . .. 155
Simulaci6n de una calculadora............................ 156
Tirada de un dado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 159
Valores entre 0 y 1....................................... 160
Numeros pseudoaleatorios - Volumen de una esfera. . . . . . . . .. 161
Creaci6n de un array unidimensional. . . . . . . . . . . . . . . . . . . . . .. 168
Nota media del curso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 169
Crear un array bidimensional. . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 170
Tanto por ciento de aprobados. . . . . . . . . . . . . . . . . . . . . . . . . . . .. 171
Encontrar el maximo y e1 minimo de un conjunto de valores. 172
Limpiar el buffer asociado con stdin. . . . . . . . . . . . . . . . . . . . . .. 177
Examinar una cadena de caracteres almacenada en memoria.. 178
Linea de texto y calcular su longitud. . . . . . . . . . . . . . . . . . . . . .. 179
Conversi6n de mayusculas a minusculas. . . . . . . . . . . . . . . . . . .. 181
Leer una lista de nombres. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 182
Funciones para manipular cadenas de caracteres. . . . . . . . . . . .. 185
Funci6n strtok. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 188
Funciones para clasificaci6n y conversi6n de datos. . . . . . . . . .. 196
Calcular el 070 de aprobados y suspensos. . . . . . . . . . . . . . . . . . .. 200
Biblioteca compuesta por libros y revistas. . . . . . . . . . . . . . . . . .. 204
Campos de bits : . . . . . . . . . . . . . . . . . . . . . . .. 208
Tabla de frecuencias de letras adyacentes en un texto. . . . . . . .. 211
Cambio de atributos utilizando campos de bits. . . . . . . . . . . . .. 212
Manipulaci6n de un valor float bit a bit. . . . . . . . . . . . . . . . . . .. 216
Visualizar el contenido de un bloque de memoria. . . . . . . . . . .. 224
Escribir los valores de un array. . . . . . . . . . . . . . . . . . . . . . . . . . .. 226
Funci6n "longstr" que devuelve la longitud de una cadena. .. 228
Funci6n para copiar una cadena en otra. . . . . . . . . . . . . . . . . . .. 230
Array de dos dimensiones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 234
Funci6n que devuelve el nombre del mes 1 a 12 dado. . . . . . . .. 236
Clasificar cadenas de caracteres. . . . . . . . . . . . . . . . . . . . . . . . . . .. 237
Asignaci6n de espacio para cadenas de caracteres. . . . . . . . . . .. 241
Asignaci6n de espacio para un array de enteros. . . . . . . . . . . . .. 243
Funciones real/oc y free. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 245
Punteros a estructuras. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 247
Busqueda Secuencial..................................... 260
Programa Alumnos...................................... 262
Leer una fecha, verificarla y escribirla con formato. . . . . . . . .. 265
Paso de parametros por referencia, utilizando punteros. . . . . .. 268
Pasando arrays a funciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 269
Linea de texto mas larga. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 270
Pasando punteros a funciones............................. 272
Argumentos en linea de 6rdenes. . . . . . . . . . . . . . . . . . . . . . . . . .. 275
Funciones con un numero de argumentos variable. . . . . . . . . . .. 278
Calculo del factorial de un numero. . . . . . . . . . . . . . . . . . . . . . . .. 280
Fusionar dos listas clasificadas. . . . . . . . . . . . . . . . . . . . . . . . . . . .. 282
Numero de veces que aparece cada letra en una cadena. . . . . .. 284
Calendario perpetuo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 286
Punteros a funciones..................................... 290
Funci6n matherr......................................... 298
Generar un numero aleatoric cada segundo. . . . . . . . . . . . . . . .. 301
Clasificaci6n de los elementos de una lista. . . . . . . . . . . . . . . . .. 304
Busqueda de un elemento en una lista. . . . . . . . . . . . . . . . . . . . .. 307
Clasificar lexicograficamente 0 numericamente. . . . . . . . . . . . . .. 309
Enscribir datos en un fichero caracter a caracter. . . . . . . . . . . .. 327
Leer datos de un fichero caracter a caracter. . . . . . . . . . . . . . . .. 328
Contar los caracteres de un fichero. . . . . . . . . . . . . . . . . . . . . . . .. 329
Escribir y leer datos en un fichero palabra a palabra. . . . . . . .. 331
Entrada/salida de cadenas de caracteres. . . . . . . . . . . . . . . . . . . .. 333
Escribir el contenido de un fichero por la impresora. . . . . . . . .. 335
Escribir y leer datos con formato en un fichero. . . . . . . . . . . . .. 336
Escribir y leer datos en un fichero registro a registro 339
Control del buffer asociado a un fichero. . . . . . . . . . . . . . . . . . .. 342
Acceso aleatorio a un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 350
Funci6n buscar.......................................... 353
Reproducci6n de la orden copy. . . . . . . . . . . . . . . . . . . . . . . . . . .. 364
Proceso de ficheros aleatorios con funciones de bajo nivel. . .. 367
Funciones para 1a consola. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 378
Presentaci6n de un menu......... . . . . . . . . . . . . . . . . . . . . . . .. 381
Emitir un sonido por el altavoz. . . . . . . . . . . . . . . . . . . . . . . . . . .. 387
Compilaci6n condicional. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 397
Utilizando ficheros de cabecera. . . . . . . . . . . . . . . . . . . . . . . . . . .. 403
Utilizando el preprocesador............................... 405
Medir tiempos de ejecuci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 406
Funciones intrinsecas..................................... 407
Macros y Funciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 408
Crear objetos.............................. . . . . . . . . . . . . .. 417
Operaciones con listas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 423
Programa calculadora. Aplicaci6n de pilas. . . . . . . . . . . . . . . . .. 429
Realizaci6n de sucesos. Aplicaci6n de colas. . . . . . . . . . . . . . . .. 433
Listas circulares. Suma de ecuaciones algebraicas. . . . . . . . . . . .. 439
Lista doblemente enlazada ordenada ascendentemente. . . . . . .. 444
Funciones recursivas para recorrer un arbol. . . . . . . . . . . . . . . .. 453
Arbol binario de busqueda. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 455
Borrar un nodo cualquiera de un arbol. . . . . . . . . . . . . . . . . . . .. 459
Arbol perfectamente equilibrado......................... .. 461
Funci6n de Ackerman recursiva. . . . . . . . . . . . . . . . . . . . . . . . . . .. 467
Funci6n de Ackerman no recursiva. . . . . . . . . . . . . . . . . . . . . . . .. 468
Torres de Hanoi......................................... 472
Clasificar lineas de texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 474
Ordenaci6n por inserci6n................................. 478
Ordenaci6n Quicksort.................................... 481
Busqueda Binaria........................................ 486
Ordenar un fichero. Acceso secuencial. . . . . . . . . . . . . . . . . . . . .. 490
Metodo de ordenaci6n Quicksort para ficheros... . . . . . . . . . .. 495
Metodo hash abierto............................ 503
Rellenar una ventana en la pantalla con el caracter ,car. . . . . .. 512
Punteros nulos.......................................... 524
Variables y punteros basados en un segmento constante. . . . . .. 532
Punteros basados en un segmento variable. . . . . . . . . . . . . . . . .. 533
Puntero basado en otro puntero. . . . . . . . . . . . . . . . . . . . . . . . . .. 536
Puntero basado en void. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 538
Puntero basado en su propio segmento................... .. 539
Manipulaci6n de areas de memoria , , . . .. 544
Un ejemplo de trabajo con librerias. . . . . . . . . . . . . . . . . . . . . . .. 577
Fichero de descripciones. NMAKE......................... 580
Un ejemplo de trabajo con la utilidad NMAKE. . . . . . . . . . . . .. 593
Pagina activa.... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 620
Pulsar una tecla para continuar. . . . . . . . . . . . . . . . . . . . . . . . . . .. 621
Posicionar el cursor en la fila y columna indicadas. . . . . . . . . .. 627
Raiz cuadrada de un numero entero (_asm). . . . . . . . . . . . . . .. 631
Llamando a funciones C desde un bloque _asm. . . . . . . . . . .. 633
. Inicializar un array. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 634
Utilizaci6n de punteros en bloques _asm. . . . . . . . . . . . . . . . . .. 635
Trabajando con estructuras desde un bloque _asm. . . . . . . . .. 638
Utilizando etiquetas C y _asm. . . . . . . . . . . . . . . . . . . . . . . . . . .. 640
Raiz cuadrada de un numero entero. . . . . . . . . . . . . . . . . . . . . . .. 645
Leer datos del puerto serie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 654
Llamadas al DOS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 656
Servicios de impresora. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 662
Servicios del BIOS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 666
Visualizar los ficheros .EXE de un directorio dado. . . . . . . . . .. 676
Ejecutar 6rdenes del DOS con la funci6n system. . . . . . . . . . . .. 678
Cambiando los atributos de un fichero...... . 681
Programa residente (TSR). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 693
Funci6n atexit........................................... 703
Manipulaci6n de errores en coma flotante. . . . . . . . . . . . . . . . . .. 705
Manipulaci6n de senales de interrupci6n. . . . . . . . . . . . . . . . . . .. 710
Utilizaci6n de la funci6n exec. . . . . . . . . . . . . . . . . . . . . . . . . . . .. 714
Utilizaci6n de la funci6n spawn. . . . . . . . . . . . . . . . . . . . . . . . . . .. 718
Estructura de un programa gnifico. . . . . . . . . . . . . . . . . . . . . . . .. 727
Modos de video disponibles en tu ordenador. . . . . . . . . . . . . . .. 731
Color de fondo y color del texto. . . . . . . . . . . . . . . . . . . . . . . . . .. 735
Color de fondo y de primer plano. . . . . . . . . . . . . . . . . . . . . . . .. 737
Sistema de coordenadas 16gicas.. . . . . . . . . . . . . . . . . . . . . . . . . .. 740
Alternar entre paginas de video. . . . . . . . . . . . . . . . . . . . . . . . . . .. 745
Portada para Microsoft C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 756
Colorear figuras......................................... 759
Ventanas de texto..................... . . . . . . . . . . . . . . . . . .. 763
Funciones para animaci6n de figuras. . ...•.. 767
Animaci6n - pe10ta rodando. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 769
Choque de una pelota contra una barrera. . . . . . . . . . . . . . . . . .. 770
Bola de billar........................................... (72
Representaci6n de la funci6n radio = cos(2 * alfa). . . . . . . . . .. 776
Representaci6n de la funci6n Y=2*cos(X)A2-sin(5+X). . . . . . .. 777
Representaci6n grafica utilizando tres ventanas. . . . . . . . . . . . .. 780
Presentaciones graficas - diagramas de barras y sectores. . . . .. 789
Presentaciones graficas multiples.............. 791
Presentaciones graficas por puntos......................... 794
Demostraci6n de tipos de letras. . . . . . . . . . . . . . . . . . . . . . . . . . .. 800
Este es un libro que pretende cubrir dos objetivos: ser un manual para
aprender C y ser una guia para el usuario de C.
La forma en la que se ha estructurado ellibro ha sido precisamente,
pensando en ese primer objetivo. El libro se ha dividido en veinticuatro
capitulos que van presentando ellenguaje poco a poco, empezando por
10 mas sencillo, presentando cada tema a su tiempo, hasta llegar al final
donde se habra visto todo 10 referente a la programacion en C y utilidades,
sin apenas encontrar dificultades.
El segundo objetivo queda conseguido al incluir en este libro todo 10
que un usuario quiere saber respecto a C, esto es, explicacion y desarrollo
de todas las sentencias, estructuras, punteros, funciones, ficheros y direc-
trices para compilador. Se completa el estudio de C con un capitulo refe-
rente a estructuras dinamicas y otro de algoritmos de uso comun. Final-
mente se estudian tecnicas avanzadas que abordan la materia referente a
manejo de la memoria, compilacion yenlace, librerias, rutinas en lenguaje
ensamblador, utilidades como el depurador de C, servicios del DOS y del
BIOS, grcificos y una explicacion para el manejo del entorno de programa-
cion del paquete de Microsoft C.
Microsoft eversion 6, producto desarrollado por Microsoft, es un com-
pilador C desarrollado para los ordenadores personales IBM y compati-
1. Introducci6n al lenguaje C
2. Elementos del lenguaje C
3. Comenzando con ellenguaje C
4. Sentencias de control
5. Tipos estructurados de datos
6. Punteros
7. Funciones
8. Funciones estandar de E/S
9. Funciones de E/S de bajo nivel
10. Funciones para la consola y puertos de entrada salida
11. El preprocesador de C
12. Estructuras dinamicas de datos
13. Algoritmos recursivos, de ordenaci6n y de busqueda
14. Manejo de la memoria
15. Compilar y enlazar
16. Librerias y utilidades del compilador
17. Rutinas en lenguaje ensamblador
18. Comunicaciones. Servicios del DOS y del BIOS
19. C y DOS
20. Control de procesos
21. Grcificos con C
22. Representaciones grcificas
23. Utilizaci6n del PWB
24. Instalaci6n de Microsoft C
A. Ficheros .h de C
B. C6digos de caracteres (ASCII)
C. Indice alfabetico
bles IBM. Este compilador ademas de correr bajo MSDOS y OS/2, inclu-
ye tambien soporte para Microsoft Windows.
Este libro posee varias caracteristicas dignas de resaltar. Es breve en
teoria y abundante en ejemplos, 10 que Ie hara aun mas facil: el aprendiza-
je para aquellas personas que quieren iniciarse en el tema, 0 la busqueda
y compresi6n de un tema puntual para aquellas otras personas entendidas
en programaci6n C. La metodologia utilizada en el desarrollo de los pro-
gramas es la descomposici6n arriba-abajo (top down). Es un libro facil de
entender.
La materia total que compone la Enciclopedia dellenguaje C, se ha
dividido en los siguientes capitulos y apendices:
Todo esto se ha documentado con alrededor de 175PROBLEMAS RE-
SUELTOS, utilizando la programaci6n estructurada, muchos de ellos vali-
dos como parte integrante en el desarrollo de aplicaciones.
He recibido ayuda de algunas personas durante la preparaci6n de este
libro, y por ello estoy francamente agradecido. En especial quiero expresar
mi agradecimiento a la firma Microsoft, por la ayuda material que me ha
prestado.
PARTE
1
Programaci6n con el Lenguaje C
• Introducci6n al Lenguaje C
• Elementos del Lenguaje C
• Comenzando con el Lenguaje C
• Sentencias de Control
• Tipos Estructurados de Datos
• Punteros
• Funciones
EI C es un lenguaje de programaci6n de prop6sito general. Sus principales
caracteristicas son:
- Programaci6n estructurada.
- Economia en las expresiones.
- Abundancia en operadores y tipos de datos.
Codificaci6n en alto y bajo nivel simultaneamente.
Reemplaza ventajosamente la programaci6n en ensamblador.
Utilizaci6n natural de las funciones primitivas del sistema.
No esta orientado a ningun area en especial.
Producci6n de c6digo objeto altamente optimizado.
- Facilidad de aprendizaje.
Ellenguaje C naci6 en los Laboratorios Bell de AT&Ty ha sido estre-
chamente asociado con el sistema operativo UNIX, ya que su desarrollo
se realiz6 en este sistema y debido a que tanto UNIX como el propio com-
pilador C y la casi totalidad de los programas y herramientas de UNIX,
fueron escritos en C. Su eficiencia y claridad han hecho que el lenguaje
ensamblador apenas haya sido utilizado en UNIX.
Este lenguaje esta inspirado en ellenguaje B escrito por Ken Thomp-
son en 1970con intenci6n de recodificar el UNIX, que en la fase de arran-
que estaba escrito en ensamblador, en vistas a su transportabilidad a otras
maquinas. B era un lenguaje evolucionado e independiente de la maquina,
inspirado en ellenguaje BCPL concebido por Martin Richard en 1967.
En 1972,Dennis Ritchie, toma el relevoy modifica ellenguaje B, crean-
do ellenguaje C y reescribiendo el UNIX en dicho lenguaje. La novedad
que proporcion6 ellenguaje C sobre el B fue el disefio de tipos y estructu-
ras de datos.
Los tipos basicos de datos eran char (caracter), int (entero),jloat (reales
en simple precisi6n) y double (reales en doble precisi6n). Posteriormente
se afiadieron los tipos short (enteros de longitud ~ longitud de un int),
long (enteros de longitud ~ longitud de un int), unsigned (enteros sin sig-
no) y enumeraciones. Los tipos estructurados basicos de C son las estruc-
turas, las uniones y los arrays. Estos permiten la definici6n y declaraci6n
de tipos derivados de mayor complejidad.
Las instrucciones de control de flujo de C son las habituales de la pro-
gramaci6n estructurada: if, for, while, switch-case, todas incluidas en su
predecesor BCPL.
C incluye tambien punteros y funciones. Los argumentos de las fun-
ciones se pasan por valor, esto es copiando su valor, 10 cual hace que no
se modifiquen los valores de los argumentos en la Hamada. Cuando se de-
sea modificar los argumentos en la Hamada, estos se pasan por referencia,
es decir, se pasan las direcciones de los argumentos. Por otra parte, cual-
quier funci6n puede ser Hamada recursivamente.
Una de las peculiaridades de C es su riqueza de operadores. Puede
decirse que practicamente dispone de un operador para cada una de las
posibles operaciones en c6digo maquina.
Hay toda una serie de operaciones que pueden hacerse con ellenguaje
C, que realmente no estan incluidas en el compilador propiamente dicho,
sino que las realiza un preprocesador justa antes de cada compilaci6n. Las
dos mas importantes son # define (directriz de sustituci6n simb6lica 0 de
definici6n) e # include (directriz de inclusi6n en e1 fichero fuente).
Finalmente, C, que ha sido pensado para ser altamente transportable
y para programar 10improgramable, igual que otros lenguajes tiene sus in-
convenientes. Carece de instrucciones de entrada/salida, de instrucciones
para manejo de cadenas de caracteres, con 10 que este trabajo queda para
la libreria de rutinas, con la consiguiente perdida de transportabilidad. La
excesivalibertad en la escritura de los programas puede llevar a errores en
la programaci6n que, por ser correctos sintacticamente no se detectan a
simple vista. Por otra parte las precedencias de los operadores convierten
a veces las expresiones en pequenos rompecabezas. A pesar de todo, C ha
demostrado ser un lenguaje extremadamente eficaz y expresivo.
Este lenguaje ha evolucionado paralelamente a UNIX, que a su vez
ha pasado por diversas versiones entre las que destaca la de Microsoft con
su XENIX para micros de 16 bits.
En este apartado se van a exponer los pasos a seguir en la realizaci6n de
un programa, por medio de un ejemplo. La siguiente figura, representa es-
tos pasos en el orden en el que hay que ejecutarlos.
El ejemplo de la figura indica que una vez editados los ficheros fuente
a.c y b.c, son compilados obteniendose los ficheros objeto a.obj y b.obj los
cuales son enlazados con el fichero c.obj, con la libreria d.lib y con las li-
brerias del sistema .lib dando lugar a un unico fichero ejecutable a.exe.
La orden correspondiente para compilar y enlazar los ficheros expuestos
en este ejemplo, es la siguiente:
Para ejecutar el fichero a.exe resultante, escribir el nombre de dicho
fichero (a), y pulsar Enter.
C
0
E a.c M a.obj
D P
I I
T
L
0
A
D
R b.c 0 b.obj
R
E
N
L
c.obj A a.exe
Z
A
D
0
d.lib R
Para editar un programa, primeramente llamaremos, para su ejecucion, al
programa editor 0 procesador de textos que vayamos a utilizar. Podemos
utilizar el procesador de textos suministrado con el compilador 0 nuestro
propio procesador (ver capitulo 23). El nombre del fichero para salvar el
programa en el disco, debe tener como extension .c.
El paso siguiente, es escribir el texto correspondiente al program a fuen-
te. Cada sentencia dellenguaje C finaliza con un punto y coma y cada li-
nea del programa la finalizamos pulsando la tecla Enter.
Como ejercicio para practicar 10 hasta ahora expuesto, escribir el si-
guiente ejemplo:
CAPITULO I: INTRODUCCION AL LENGUAJE C 43
# include <stdio.h >
# include <stdlib.h>
main( )
(
char *mensajel
char *mensaje2
char nombre[50];
HBienvenido a C':·
HTe alegrard el haberme conocido";
system(Hcls");
printft';,Cudl es tu nombre? ");
gets(nombre);
printj("n%s %sn%sn': mensajel, nombre, mensaje2);
J
Comentamos brevemente cada linea de este programa. No apurarse si al-
gunos de los terminos no quedan muy claros ya que todo ellos se venin
con detalle en capitulos posteriores.
Las dos primeras lineas incluyen las declaraciones necesarias para las
funciones que aparecen en el programa. Estas funciones son: system( ),
printf( ) y gets( ).
A continuaci6n se escribe la funci6n principal main( ). Las dos pri-
meras Hneas de esta, definen las cadenas de caracteres mensajel y mensaje2
y la siguiente linea define la cadena de caracteres nombre para contener
49 caracteres mas el caracter de "fin de cadena" que aftade C automati-
camente.
La funci6n printj( ) escribe el contenido de las variables especificadas
en la misma.
La funci6n gets( ) permite introducir datos a traves del teclado para
la variable especificada, en este caso nombre.
El programa editado esta ahora en la memoria. Para que este trabajo pue-
da tener contir.uidad, el programa escrito se debe grabar en el disco utili-
zando la orden correspondiente del editor.
El siguiente paso es compilar el programa, esto es, traducir el programa
fuente a lenguaje maquina para posteriormente enlazarlo con las librerias
de C y obtener as! un programa ejecutable. Estas operaciones, compilar
y enlazar, se efectuan mediante la orden c/.
Al compilar un programa, se nos pueden presentar errores de compi-
lacion, debidos a que el programa escrito no se adapta a la sintaxis y reglas
del compilador. Estos errores se iran corrigiendo hasta obtener una com-
pilacion sin errores.
Cada vez que se realiza el proceso de compilacion y enlace del programa
actual, C genera automaticamente sobre el disco un fichero con extension
.exe. Este fichero puede ser ejecutado directamente desde el DOS, sin el
soporte de C, escribiendo el nombre del fichero .exe despues del prompt
del DOS y pulsando Enter a continuacion.
Cuando se crea un fichero ejecutable, C utiliza primero el compilador
para compilar el programa fuente, dando lugar a un fichero intermedio co-
nocido como fichero objeto (.obj). A continuacion C utiliza el programa
Iink.exe para unir, en un unico fichero ejecutable, el modulo 0 los modulos
del programa compilados separadamente y las rutinas de las librerias del
compilador C que el programa necesite.
Al ejecutar el programa, pueden ocurrir errores durante la ejecucion.
Por ejemplo, puede darse una division par cero. Estos errores solamente
pueden ser detectados por C cuando se ejecuta el programa y senin notifi-
cados con el correspondiente mensaje de error.
Hay otro tipo de errores que no dan lugar a mensaje alguno. Por ejem-
plo: un programa que no termine nunca de ejecutarse, debido a que pre-
senta un lazo, donde no se llega a dar la condicion de terminacion. Para
detener la ejecucion se tienen que pulsar las teclas Ctrl +C.
Un programa una vez ejecutado puede dar lugar a una solucion incorrec-
ta. Este caso exige un analisis minucioso de como se desarrolla el progra-
ma en su ejecucion; esto es, hay que entrar en la fase de depuracion del
programa.
La forma mas sencilla y eficaz para realizar este proceso, es utilizar
un programa depurador. En el capitulo 16, se explica como utilizar el de-
purador Code View de Microsoft.
Vamos a preparar un programa formado par un solo modulo fuente, para
depurarlo. El primer paso sera editar el programa. Como ejemplo escribit
el siguiente programa, e1cual, imprime el valor y su cuadrado, de cada uno
de los elementos de una matriz numerica de 5 filas y 3 columnas, y cuenta
e imprime el numero de elementos que son pares.
#include <stdio.h>
#define FILAS 5
#define COLS 3
void display( iot n )
[
iot cuadrado;
cuadrado = n *n;
printj("el cuadrado de %2d es %3d n': n, cuadrado);
J
iot numero_par( iot x )
[
if (x % 2 = = 0)
retu rn (1);
else
return (0);
main( )
[
static iot a[FILAS][COLS] =
[1,2,3,4,5,6, 7,8,9,10,11,12,13,14,15 j;
iot fila, columna;
for (fila = 0; fila < FILAS; fila + +)
for (columna = 0; columna < COLS; columna + +)
[
display( aUila][columna] );
if ( numero~ar(aUila][columna]) )
numeros~ares+ +;
J
printj(" n  nTotal numeros pares: %d n': numeros_pares);
j
Una vez editado salvamos el programa en el disco. Llamemosle por
ejemplo progOl02.c.
Fijandonos en la funci6n principal, main(), vemos que definimos una ma-
triz cuadrada cca': para a continuaci6n recorrerla elemento a elemento por
filas.
Esta funci6n principal llama a la funci6n display( ) que escribe el va-
lor del elemento objeto de analisis y su cuadrado, y llama tambien a la fun-
cion numero_par( ) que indica si el numero es 0 no par. En el caso de
que sea par incrementamos en una unidad el contador numeros_pares.
Una vez que se han analizado todos los elementos de la matriz, se es-
cribe el numero de valores pares encontrados.
Como siguiente paso, compilaremos el programa con las opciones IZi
y IOd.
La opcion IZi hace que se incluya en el fichero ejecutable resultante,
informacion necesaria para realizar la depuracion y la opcion IOd impide
la optimizacion, la cual puede dificultar la depuracion.
Cuando finaliza el proceso de compilacion y enlace, invocamos al depura-
dor (debug).
Las operaciones minimas que debe incluir un depurador son las si-
guientes:
Permite ver la sentencia del programa que es ejecutada. Code View
incluye las siguientes opciones:
Ejecutar una sentencia cada vez, incluidas funciones definidas
por el usuario. Esta modalidad se activa y se continua, pulsan-
do la tecla F8. Si no queremos que las funciones se ejecuten sen-
tencia a sentencia pero sf la funci6n principal main( ), utilizar
la tecla FIO.
Si pulsamos la tecla F5, la ejecuci6n continua hasta el final del
programa 0 hasta el primer punto de parada, si este existe.
Un punto de parada es una pausa que se hace en un lugar determi-
nado dentro del programa. Esto permite testear los valores de las
variables en ese instante. Colocar los puntos de parada donde se sos-
peche que esta el error.
Para poner 0 quitar una pausa, se coloca el cursor en ellugar don-
de va a tener lugar la pausa y se pulsa F9.
Las expresiones de seguimiento permiten observar los valores, de las
variables 0 de expresiones del programa, mientras este se ejecuta (6r-
denes Add Watch, Delete Watcha, ... ).
Ejecutar la orden Add Watch ... y escribir en el recuadro corres-
pondiente, fila. Realizar la misma operaci6n para incluir en la
ventana de seguimiento columna y aUilaJ[columnaj.
Continuar la ejecuci6n pulsando F8 0 FIO. Si se pulsa la tecla
F5, la ejecuci6n del programa continua hasta el final del pro-
grama 0 hasta un punto de parada si se encuentra.
El nombre de un fichero consta de dos partes: el nombre base que puede
tener hasta ocho caracteres y la extension que puede tener hasta tres carac-
teres y va separada del nombre base por un punto. C identifica las siguien-
tes extensiones con los ficheros que a continuaci6n se indican:
.obj fichero resultante de la compilaci6n de un fichero fuente.
No es ejecutable .
.mak fichero que contiene una lista de m6dulos y las acciones
que con ellos debe hacerse para construir el programa final.
Cuando se especifica el nombre de un fichero sin extensi6n, C asume
por defecto la extensi6n .obj.
Las palabras clave aparecenln en negra y deben escribirse exactamente como
aparecen.
EI texto que no aparece en negra, significa que ahi debe ponerse la
informacion indicada por ese texto.
Los puntos suspensivos "..." indican que pueden aparecer mas elementos
de la misma forma.
Cuando dos 0 mas opciones aparecen entre Haves "{ ]" separadas por
"I", se elige una, la necesaria dentro de la sentencia.
Estos caracteres son utilizados para formar las constantes, los identi-
ficadores y las palabras clave de C.
El compilador C trata las letras mayusculas y minusculas como carac-
teres diferentes. Por ejemplo los identificadores Pi y PI son diferentes.
Espacio en blanco, tabulador horizontal (HT), tabulador vertical (VT), avan-
ce de pagina (FF), y nueva linea (LF 0 CR + LF) son caracteres denomina-
dos espacios en blanco, porque la labor que desempefian es la misma que
la del espacio en blanco, esto es, actuar como separadores entre los ele-
mentos de un programa. Los espacios en blanco en exceso son ignorados
por el compilador, 10 cual nos permite escribir programas mas legibles.
EI caracler Ctr! +Z bajo DOS, (equivalente a Ctrl +0 bajo UNIX) es
tratado POl' el compiIador como un indicador de fin de fichero (End Of File).
Los caracteres tambien pueden ser representados por secuencias de escape.
Una secuencia de escape esta formada por el caracter  seguido de una
letra 0 de una combinaci6n de digitos. Son utilizadas para acciones como
nueva linea, tabular y para representar caracteres no imprimibles.
n
t
v
b
r
f
a
 '
Nueva linea
Tab horizontal
Tab vertical (s610 para impresora)
Backspace (retroceso)
Retorno de carro
Alimentaci6n de pagina (s610 para impresora)
Bell (alerta, pitido)
Comilla simple
Comilla doble
Backslash (barra invertida)
Canicter ASCII. Representaci6n octal
Caracter ASCII. Representaci6n hexadecimal

ddd
xdd
Hay varios tipos fundamentales de datos. Los ficheros de cabecera Iimits.h
y f1oat.h especifican los valores maximo y minima para cad a tipo. Los po-
demos clasificar en:
Tipos enteros: char, short, int, long y enum.
Tipos reales: float, double y long double.
Otros: void.
Cada tipo entero puede ser calificado por las palabras clave signed 0
unsigned, 10 que da lugar a tener disponibles los siguientes tipos extras:
signed char, unsigned char
signed short, unsigned short
signed int, unsigned int
signed long, unsigned long
Un entero calificado signed es un entero con signo, esto es, un ntlme-
ro entero positivo 0 negativo. Un numero entero calificado unsigned es un
numero entero sin signo, el cual es manipulado como un numero entero
positivo.
Si los calificadores signed y unsigned se utilizan sin un tipo especffi-
co, se asume el tipo into Por este motivo, las siguientes declaraciones son
equivalentes:
signed x;
signed int X;
unsigned y;
unsigned int y;
El tipo char es utilizado para almacenar un valor entero en el rango -128
a 127, correspondiente a un caracter del c6digo ASCII. Solamente los va-
lores 0 a 127 son equivalentes a un caracter.
De forma similar el tipo unsigned char puede almacenar valores en
el rango de 0 a 255, valores correspondientes a los numeros ordinales de
10s 256 caracteres ASCII.
Este ejemplo declara una variable car de tipo char, capaz de contener
un canicter cuyo c6digo ASCII se correspondeni con un valor entero entre
o y 127. Otros ejemplos son:
char a = 'z';
signed char b = Ox07;
unsigned char c = 32;
De forma similar el tipo unsigned short puede almacenar valores en
el rango de 0 a 65535 (0 a 2EI6-1).
Este ejemplo declara i y j, como variables enteras con posibilidad de
tDmar vaDres entre -'3216'il y '1'2161. Otros ejempos son:
short int a = -500;
signed short b = 1990;
unsigned short int c = OxfOOO;
Un entero es para C un numero sin punta decimal. El rango de valores de-
pende de la maquina. Igualmente ocurre con el tipo unsigned into Para una
maquina con un procesador de 16 bits el rango de valores es de:
-32768 a 32767 (-2EI5 a 2EI5-1) para el tipo into
o a 65535 ( 0 a 2EI6-1) para el tipo unsigmld.
El uso de enteros produce un c6digo compacta y rapido. Para una ma-
quina de 16 bits este tipo es equivalente al tipo short y solamente oc4pa
2 bytes de memoria. En general:
Este ejemplo declara las variables n y x de tipo entero. Otros ejemplos
son:
int a = 2000;
signed int b = -30;
unsigned int c = Oxf003;
Este tipo de numeros es id6neo para aplicaciones de gesti6n. Al igual que
los enteros, son numeros sin punta decimal comprendidos en el rango de:
-2147483648 a 2147483647 (-2E31 a 2E31-1) para el tipo long.
o a 4294967295 (0 a 2E32-1) para el tipo unsigned long.
Este ejemplo declara las variables n y m de tipo entero, pudiendo to-
mar valores entre -2147483648 y 2147483647. Otros ejemplos son:
long a = -IL;
signed long b = 125;
unsigned long int c = Oxlj00230j;
La declaraci6n de un tipo enumerado es simplemente una lista de valores
que pueden ser tornados por una variable de ese tipo. Los valores del tipo
enumerado se representanin con identificadores, que senin las constantes
del nuevo tipo.
tunes,
martes,
miercotes,
jueves,
viernes,
sabado,
domingo
hoy;
Este ejemplo declara las variables hoy y ayer del tipo enumerado
dia----semana. Estas variables pueden tomar cualquier valor de los especi-
ficados, lunes ... domingo. El valor ordinal de lunes es O. Los elementos
que aparecen enumerados en la lista son considerados como constantes
enteras.
Crear una enumeraci6n es definir un nuevo tipo de datos, denominado tipo
enumerado y declarar una variable de este tipo. La sintaxis es la siguiente:
enum tipo_enumerado
!
Despues de definir un tipo enumerado, podemos declarar una 0 m,b
variables de ese tipo, de la forma:
};
enum colores color;
Este ejemplo declara una variable color del tipo enumerado colores,
la cual puede tomar cualquier valor de los especificados en la lista.
Cada identificador, de la lista de constantes enteras en una enumera-
ci6n, tiene asociado un valor. Por defecto, el primer identificador tiene aso-
ciado el valor 0, el siguiente el valor 1, y as! sucesivamente.
A cualquier identificador de la lista, se Ie puede asignar un valor ini-
cial por medio de una expresi6n con stante. Los identificadores sucesivos
tomanln valores correlativos a partir de este.
azul, amarillo, raja, verde
color;
Este ejemplo define un tipo enumerado Hamado colores y declara una
variable color de ese tipo. Los valores asociados a los identificadores son
los siguientes: azul = 0, amarillo =1, raja =2, verde = 0, blanco = 1
Y negro = 2.
3. Desafortunadamente, no es posible leer 0 escribir directamente un
valor de un tipo enumerado.
Estos numeros son los mas recurridos en un lenguaje de programaci6n. Un
real en simple precision es un numero que puede tener un punta decimal
y que puede estar comprendido en el rango de:
-3.402823E + 38 a -1.175494E-38 para numeros negativos
1.175494E-38 a 3.402823E+38 para numeros positivos
Un numero real en simple precision no tiene mas de 7 digitos signifi-
cativos.
Este ejemplo declara la variable x de tipo real en simple precision. Otros
ejemplos son:
float a = 3.14159;
float b = 2.2e-5;
Un numero real en doble precision es un numero que puede tener un punta
decimal y puede estar comprendido en el range de:
-1.79769E+308 a -2.22507E-308 para numeros negativos
2.22507E-308 a 1.79769E+308 para numeros positivos
Un numero real en doble precision tiene hasta 16 digitos significati-
vos. Esto da lugar a calculos mas exactos que en simple precision.
Este ejemplo declara la variable x de tipo real en doble precision. Otro~
ejemplos son:
double a = 3.1415926;
double b = 2.2e-8;
-1.189731£+4932 a -3.362103E-4932 para numeros negativos
3362103E-4932 a 1.189731£+4932 para numeros positivos
Un numero real en doble precision formato largo no tiene mas de 19
digitos significativos. Esto da lugar a calculos mas precisos que en doblc
precision.
long double X;
long double y = 3.17e+425;
El tipo void se utiliza para dec1arar funciones que no retornan un valor
o para dec1arar un puntero a un tipo no especificado. Si void aparece entre
parentesis a continuacion del nombre de una funcion, no es interpretado
como un tipo. En este caso indica que la funcion no acepta argumentos.
double jx(void);
void jy(void);
void *P;
Este ejemplo dec1ara la funcion denominada jx, como una funcion
sin argumentos que devue1ve un valor de tipo real de doble precision; la
funcionjy, como una fundon sin argumentos que no devuelve valor algu-
no; y un puntero P a un objeto de un tipo no espedficado.
Los tipos derivados son construidos a partir de los tipos fundamentales.
Algunos de ellos son los siguientes:
Un puntero es una direcci6n de memoria que indica d6nde se localiza un
objeto de un tipo especificado. Para definir una variable de tipo puntero
se utiliza el operador de indirecci6n *.
int *p,'
char *plineas[40j,'
Este ejemplo declara un puntero p a un valor entero; y un array de
punteros plineas (plineas[Oj a plineas[39J) a valores de tipo char.
Una estructura es una variable que representa 10 que normalmente cono-
cemos como registro, esto es, un conjunto de uno 0 mas campos de igual
o diferentes tipos.
float a, b;
complejo;
struct persona
{
char nombre[20j;
char apellidos[40j;
long dni;
Este ejemplo declara las variables complejo y reg, como estructuras
o registros. La variable complejo comprende los campos a y b de tipo real;
y la variable reg comprende los campos nombre y ape/lidos que son cade-
nas de caracteres y el campo dni de tipo long.
Una union tiene la misma forma de definici6n que una estructura. Las unio-
nes, a diferencia de las estructuras, representan registros variables. Esto cjuiert:
decir que una variable de este tipo, puede alternar entre varios tip os.
Un array es un conjunto de objetos, todos del mismo tipo, que ocupan po-
siciones sucesivas en memoria. Para definir un array se utiliza el operador
[ ] despues del nombre del array.
Este ejemplo declara un array !ista de 40 elementos (!ista[O]a !ista[39J)
para almacenar valores enteros.
Una funci6n es un subprograma C, el cual toma argumentos de unos tipos
dados y retorna un valor de un tipo especificado. Para declarar una fun-
ci6n se utiliza el operador ( ) despues del nombre de la funci6n.
Permite declarar nuevos nombres de tipos de datos; esto es, sin6nimos de
otms tipos ya sean fundamentales 0 derivados, los cuales pueden ser utili-
zados mas tarde para declarar variables de esos tipos.
typedef int ENTERO;
typedef int (*PFlj( );
typedef struct persona REG;
Este ejemplo propone el tipo ENTERO como sin6nimo de int; el tipo
PFI como un puntero a una funci6n que devuelve un valor entero; y el tipo
REG como sin6nimo de struct persona.
ENTERO n;
PFI p;
REG per;
declaran n como una variable de tipo entero, p como un puntero a una
funci6n que devuelve un valor entero y per como una estructura 0 registro
de tipo persona.
Las declaraciones typedej permiten parametrizar un programa para
evitar problemas de portabilidad. Utilizando typedej con los tipos que pue-
den depender de la instalaci6n, cuando se lleve el programa a otra instala-
ci6n s610 se tendnin que cambiar estas declaraciones.
Una constante es un valor que, una vez fijado por el compilador, no cam-
bia durante la ejecuci6n del programa. Una constante en C puede ser un
mimero, un canicter 0 una cadena de caracteres.
En general, si la constante es positiva, el signa + es opcional y si es
negativa, lleva el signo -. El tipo de una constante entera viene determina-
do por su valor. Tambien se puede indicar explicitamente el tipo de una
constante entera, afiadiendo los sufijos L, U, 0 UL (mayusculas 0 minus-
culas). Si el sufijo es L, su tipo es long cuando el valor puede ser represen-
tado en este tipo, si no es unsigned long. Si el sufijo es U, su tipo es unsig-
ned int cuando el valor puede ser representado en este tipo, si no es unsigned
long. Si el sufijo es UL, su tipo es unsigned long.
1522U
1000L
325UL
constante entera de tipo unsigned int
constante entera de tipo long
constante entera de tipo unsigned long
Una constante decimal puede tener uno 0 mas digitos, 0 a 9, de los
cuales el primera de ellos es distinto de cero.
4326
432600
constante entera de tipo int
constante entera de tipo long
Una constante octal puede tener lomas digitos, 0 a 7, precedidos por
o (cera). Su valor esta comprendido en el rango:
Oa077777
0100000 a 0177777
0200000 a 017777777777
020000000000 a 037777777777
para constantes tipo int
para constantes tipo unsigned int
para constantes tipo long
para constantes tipo unsigned long
Una constante hexadecimal puede tener lomas caracteres, 0 a 9 y
A a F, precedidos por Ox 0 OX (cero mas x). Su valor esta comprendido
en el rango:
OxOa Ox7FFF
Ox8000 a OxFFFF
OxlOOOOa Ox7FFFFFFF
Ox80000000 a OxFFFFFFFF
para constantes tipo int
para constantes tipo unsigned int
para constantes tipo long
para constantes tipo unsigned long
256
0400
OxJOO
-0400
-OxJOO
especifica el nO 256 en decimal
especifica el n° 256 en octal
especifica el nO 256 en hexadecimal
especifica el nO -256 en octal
especifica el nO -256 en hexadecimal
Una constante real esta formada por una parte entera, seguida por un punto
decimal, y una parte fraccionaria. Tambien se permite la notaci6n cientffi-
ca, en cuyo caso se afiade al valor una e 0 E, seguida por un exponente
positivo 0 negativo.
donde dfgitos representa cero 0 mas digitos del 0 al 9 y E 0 e es el simbolo
de exponente de la base 10 que puede ser positivo 0 negativo (2E-5 =
2 x J(f5). Si la constante real es positiva no es necesario especificar el sig-
no y si es negativa lleva el signa menos (-).
-17.24
17.244283
.008e3
27E-3
Una constante real tiene siempre tipo double, a no ser que se afiada
a la misma una f 0 F, en cuyo caso sera de tipo float, 0 una I 0 L para
indicar que es de tipo long double.
Este tipo de constantes esta formado por un unico caracter encerrado en-
tre comillas simples. Una secuencia de escape es considerada como un uni-
co canicter.
espacio en blanco
letra minuscula x
nueva linea
canicter ASCII Ese
' n'
,  x1B'
Una constante de caracteres es una cadena de caracteres encerrados entre
comillas dobles.
"Esto es una eonstante de earaeteres"
"3.1415926 "
"Paseo Pereda 10, Santander"
En el ejemplo siguiente el car<icter  n fuerza a que la cadena "0 pul-
sar Enter" se escriba en una nueva linea.
Cuando una cadena de caracteres es demasiado larga puede utilizarse
el canicter "  " como canicter de continuaci6n.
HEsta cadena de caracteres es dema 
siado larga."
Dos 0 mas cadenas separadas por un espacio en blanco sedan conca-
tenadas en una sola cadena.
printf(HPrimera cadena,"
H segunda cadena ");
Los caracteres de una cadena de caracteres son almacenados en locali-
zaciones sucesivasde memoria. Cada cadena de caracteres es finalizada auto-
m<iticamente por el caracter nulo representado por la secuencia de escape
 o.
El tipo de una cadena de caracteres es el tipo array donde cada ele-
mento es de tipo char (char [ J) y la clase de almacenamiento es static. El
numero de elementos de un array, necesarios para almacenar una cadena
de caracteres, es igual al numero de caracteres de la cadena, mas uno para
el caracter nulo de terminaci6n.
Los identificadores son nombres dados a constantes, variables, tipos, fun-
ciones y etiquetas de un programa. La sintaxis para formar un identifica-
dor es la siguiente:
10 cual indica que un identificador consta de uno 0 mas caracteres (letras,
digitos y el caracter de subrayado) y que el primer canicter debe ser una
tetra 0 el cardcter de subrayado.
Las letras pueden ser mayusculas 0 minusculas y se consideran como
caracteres diferentes; esto es, los identificadores Suma, suma y SUMA son
diferentes.
Los identificadores pueden tener cualquier numero de caracteres pero
solamente los 31 caracteres primeros, son significativos. Esto quiere decir
que un identificador es distinto de otro cuando difieren al menos en uno
de los 31 primeros caracteres.
suma
Catcuto~umeros~rimos
_ordenar
ab123
Las palabras clave son identificadores predefinidos que tienen un signifi-
cado especial para el compilador C. Un identificador definido por el usua-
rio, no puede tener el mismo nombre que una palabra clave.
auto
break
case
char
const
continue
default
do
double
else
enum
extern
float
for
goto
if
int
long
register
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
while
Ademas de las palabras clave anteriores, el compilador C de Micro-
soft tiene tambi(~n las siguientes:
_asm
_based
_cdecl
_emit
_export
~ar
~astcall
~ortran
----huge
~nterrupt
~oadds
_near
_pascal
-3averegs
_segment
-3egname
-3elf
Un comentario es una secuencia de caracteres utilizada para explicar el co-
digo fuente. Microsoft C soporta comentarios estilo C y estilo C++.
Un comentario estilo C es una secuencia de caracteres cualesquiera en-
cerrados entre los simbolos 1* y *1. Estos comentarios pueden ocupar mas
de una linea, pero no pueden anidarse. Por ejemplo:
1* Este es un comentario
* que ocupa varias
* !ineas.
*1
Un comentario estilo C+ + comienza con los caracteres II y termina
al final de la linea. Estos comentarios no pueden ocupar mas de una linea.
Por ejemplo:
Un comentario puede aparecer en cualquier lugar donde se permita
aparecer un espacio en blanco. El compilador trata un comentario como
a un espacio en blanco.
El valor de una variable, a diferencia de las constantes, puede cambiar a
10 largo de la ejecucion de un programa.
La sintaxis correspondiente a la declaraci6n de una variable es la si-
guiente:
c1ase representa una de las cuatro clases siguientes: auto, register, sta-
tic, 0 extern. La clase de una variable determina si esta tiene ca-
racter global (static 0 extern) 0 local (auto 0 register).
Una variable declarada fuera de todo bloque (conjunto de sentencias
encerradas entre ( }) es, por defecto, global y es accesible en el resto del
archivo fuente en el que esta declarada. Por el contrario, una variable de-
clarada dentro de un bloque, es por defecto local y es accesible solamente
dentro de este.
Cada variable de un programa, debe declararse antes de ser utilizada.
La declaraci6n consiste en enunciar el nombre de la variable y asociarle
un tipo. El tipo determina los valores que puede tomar la variable asi como
las operaciones que con ella pueden realizarse.
iot suma, incremento;
char car, linea/80];
char car = ( 0'; / * car igual al cardcter nulo */
iot c = 1; / * inicializar c a 1*/
Ala declaraci6n de un objeto, se puede anteponer el calificador eonst, con
el fin de hacer que dicho objeto sea, en lugar de una variable, una constante.
const int k = 12;
const int v[ j = [1, 2, 3, 4j;
A un objeto declarado como una constante no se Ie puede asignar un
valor. Por ello, al declararlo debe ser inicializado. Si k ha sido declarado
como constante, las siguientes sentencias darian lugar a un error:
k = 100;
k+ +;
/ * error */
/ * error */
Una declaraci6n de un puntero precedida por eonst, hace que el obje-
to apuntado sea una constante, no sucediendo 10 mismo con el puntero.
const char *pe = "abed";
pe[Oj = 'z'; / * error */
pe = "efg"; / * eorreeto */
Si 10 que se pretende es declarar un puntero como una con stante, pro-
cederemos asi:
char *const pe = "abed";
pe[Oj = 'z'; / * eorreeto */
pe = "efg";' / * error */
Para hacer que tanto el puntero como el objeto apuntado sean cons-
tantes, procederemos como se indica a continuaci6n:
const char *const pe = "abed";
pe[Oj = 'z'; h error */
pe = "efg"; / * error */
A la declaraci6n de un objeto, se puede anteponer el calificador volatile,
con el fin de hacer que dicho objeto pueda ser modificado por otros pro-
cesos diferentes al programa actual. Su utilizaci6n tiene sentido, por ejem-
plo, en procesos concurrentes. Los calificadores const y volatile, pueden
utilizarse conjuntamente 0 individualmente.
Este ejemplo declara la variable v, entera (int), accesible desde cual-
quier parte (extern), no modificable por el programa donde esta declarada
(const), pero si modificable por otros procesos (volatile).
Una expresi6n es una secuencia de operadores y operandos que especifi-
can una operaci6n determinada.
++a
suma+ =c
cantidad * precio
7 * sqrt(a) - b / 2
Los operadores son simbolos que indican como son manipulados 10s da-
tos. Se pueden clasificar en 10ssiguientes grupos: aritmeticos, 16gicos, re-
lacionales, unitarios, 16gicospara manejo de bits, de asignaci6n, operador
ternario para expresiones condicionales y otros.
Division. Los operandos pueden ser enteros 0 reales. Si am-
bos operandos son enteros el resultado es entero. En el res-
to de los casos el resultado es real.
Modulo 0 resto de una division entera. Los operandos tie-
nen que ser enteros.
int a = 10, b = 3, c;
float x = 2.0, y;
y=x+a;
c=a/b;
c=a%b;
y=a/b;
/ * el resultado es 12.0 de tipo float */
/ * el resultado es 3 de tipo int */
/ * el resultado es 1de tipo int */
/ * el resultado es 3 de tipo into Se
convierte a float para asignarlo a y */
AND. Da como resultado el valor logico 1 si ambos ope-
randos son distintos de cero. Si uno de ellos es cero el re-
sultado es el valor logico O.Si el primer operando es igual
acero, el segundo operando no es evaluado.
OR. El resultado es 0 si ambos operandos son O. Si uno de
los operandos tiene un valor distinto de 0, el resultado es
1. Si el primer operando es distinto de cero, el segundo ope-
rando no es evaluado (ASCII 124).
NOT. EI resultado es 0 si el operando tiene un valor distin-
to de cero, y 1 en caso contrario.
EI resultado es de tipo into Los operandos puedenser enteros, reales
a punteros.
p&& q
p II q
!p
da como resultado 0
da como resultado 1
da como resultado 0
Una expresi6n de Boole da como resuItado Ios vaIores I6gicos 0 0 1. Los
operadores que intervienen en una expresi6n de Boole pueden ser: opera-
dores 16gicos y operadores de relaci6n.
int p, q;
float x = 15, y
p = x = = y; / * resultado p 0 */
q = (x < y) && (y < = z); /* resultado q 1 */
Cambia de signo aI operando (compIemento ados). EI ope-
rando puede ser entero 0 real.
CompIemento a 1.EI operando tiene que ser entero (canlc-
ter ASCII 126).
Los operandos para este tipo de operaciones tienen que ser de tipo en-
tero (char, int, long, 0 enum), no pueden ser reales.
a a & 0177; / *pone a cera todos los bits de a */
/ * excepto los 7 bits de menor peso */
a a I m; / *pone a 1 todos los bits de a que */
/ * estdn a 1 en m */
a a & -077; / *pone a alas 6 bits de menor peso de a */
En las operaciones de desplazamiento el primer operando es despla-
zado tantas posiciones como indique el segundo. Si el desplazamiento es
a izquierdas, se rellena con ceros por la derecha; si el desplazamiento es
a derechas, se rellena con ceros por la izquierda si el operando es de tipo
unsigned, en otro caso se rellena con el bit de signo.
c=a«1;
d=b»1;
/* c = 6 */
/* d = -2 */
En una operaci6n de asignaci6n, el valor de la derecha, es convertido
al tipo del valor de la izquierda.
x++;
++x;
x --n;
x = n--;
i += 2;
x *= n - 3
/ * incrementa el valor de x en 1 */
/ * incrementa el valor de x en 1 */
/ * decrementa n en 1 y asigna el resultado a x */
/ * asigna el valor de n a x y despues */
/ * decrementa n en 1 */
/ * realiza la operacion i = i + 2 */
h realiza la operacion x = x * (n-3) y no */
/ * x = x * n - 3 */
/ * realiza la operacion n = n > > 1 la cual des- */
/ * plaza el contenido de n un bit a la derecha */
C tiene un operador ternario (?:), que se utiliza en expresiones condiciona-
les, las cuales tienen la forma:
La expresi6n operandol debe ser de tipo entero, real 0 puntero. La eva-
luaci6n se realiza de la siguiente forma:
• Si el resultado de la evaluaci6n de operandol es distinta de 0, el re-
sultado de la expresi6n condicional es operando2.
• Si el resultado de la evaluaci6n de operandol es 0, el resultado de
la expresi6n condicional es operando3.
Este ejemplo dice que si a > b entonces mayor = a, en caso contra-
rio, mayor = b.
Un par de expresiones separadas por una coma son evaluadas de izquierda
a derecha. Todos los efectos de la expresi6n de la izquierda son ejecutados
antes de evaluar la expresi6n de la derecha, a continuaci6n el valor de la
expresi6n de la izquierda es descartado. El tipo y el valor del resultado son
el tipa y el valor del operando de la derecha.
aux = vi, vi = v2, v2 = aux;
for (a = 256, b = i; b < 512; a/=2, b *=2)
Este operador accede a un valor indirectamente a traves de un puntero. El
resultado es el valor direccionado por el operando.
Este operador da la direcci6n de su operando. Este operador no se puede
aplicar a un campo de bits perteneciente a una estructura 0 a un identifica-
dor declarado con el calificador register.
int *pa, b;
int a[10};
/ * pa es un puntero a un valor entero d
/ * a es una array de 10 elementos de tipo int ':'/
/ * en el puntero pa se almacena la direcci6n */
/ * del 7° elemento del array a ':'/
/ * a b se Ie asigna el valor almacenado en la */
/ * direcci6n especijicada por pa ':'/
Este operador da como resultado el tamafio en bytes de su operando 0 de
un objeto del tipo especificado (se entiende por byte el espacio requerido
para almacenar un canicter). El resultado es una constante de tipo size_t
(un entero sin signo), el cual esta definido en el fichero <stdef.h >. La
sintaxis es:
donde expresi6n es un identificador 0 un nombre de un tipo de datos. Los
parentesis son opcionales, excepto cuando la expresi6n se corresponde con
un tipo de datos.
"primera cadena':
"segunda cadena':
"tercera cadena"
];
const int cadenas = (size of cadena)/(sizeof cadena[Oj);
En este ejemplo, cadena representa un array de punteros a objetos de
tipo char. Puesto que el numero de elementos (punteros) no se ha espeeifi-
cado, una forma faeil de calcularlo es mediante la operaci6n que se expre-
sa a continuaci6n. El resultado se deposita en cadenas que ha side defini-
da como una constante (const).
La tabla que se presenta a continuaci6n, resume las reglas de priori dad y
asociatividad de todos los operadores. Los operadores escritos sobre una
misma linea tienen la misma prioridad. Las lineas se han colocado de ma-
yor a menor prioridad.
Una expresi6n entre parentesis, siempre se evalua primero. Los paren-
tesistienen mayor prioridad y son evaluados de mas internos a mas externos.
() [ ] ->
* & ++
:>
* / 070
+
« »
< <= > >=
-- I-.-
&
II.
Cuando los operandos dentro de una expresi6n son de tipos diferentes, se
convierten a un tipo comun, de acuerdo con las reglas que se exponen a
continuaci6n.
Las reglas que se exponen, se aplican en ese orden, para cad a opera-
cion bin aria perteneciente a una expresion, siguiendo el orden de evalua-
cion expuesto anteriormente.
2. Si un operando es de tipo long double, el otro operando es con-
vertido a tipo long double.
3. Si un operando es de tipo double, el otro operando es convertido
a tipo double.
5. Cualquier operando de tipo unsigned char 0 unsigned short es con-
vertido a tipo unsigned into
6. Si un operando es de tipo unsigned long, el otro operando es con-
vertido a unsigned long.
7. Si un operando es de tipo long, el otro operando es convertido
a tipo long.
8. Si un operando es de tipo unsigned int, el otro operando es con-
vertido a tipo unsigned into
long a;
unsigned char b;
int c;
float d;
int 1;
Este ejemplo, teniendo en cuenta que primero se realiza la multiplica-
cion, desputs la division y por ultimo la suma, se desarrollarfa de la forma
siguiente:
2. c es convertido a unsigned int (paso 8). Se ejecuta la multiplica-
ci6n (*) y se obtiene un resultado de tipo unsigned into
4. El resultado de b * c, es convertido a double (paso 3). Se ejecuta
la divisi6n (I) y se obtiene un resultado de tipo double.
5. a es convertido a double (paso 3). Se ejecuta la suma (+) Yse ob'-
tiene un resultado de tipo double.
6. El resultado de a + b * c / d, para ser asignado a 1, es pasado
a entero por truncarniento, esto es, eliminando la parte fraccionaria.
• Los operandos que intervienen en una determinada operaci6n, son
convertidos al tipo del operando de precisi6n mas alta.
• En una asignaci6n, el valor de la parte derecha es convertido al tipo
del valor de la parte izquierda, de acuerdo con las siguientes reglas:
Los caracteres se convierten a enteros con 0 sin extensi6n de sig-
no, dependiendo esto de la instalaci6n. Bajo Microsoft C la con-
versi6n se hace con extensi6n de signo.
Los enteros se convierten a caracteres preservando los bits de me-
nor peso, esto es desechando los bits de mayor peso en exceso.
Los reales son convertidos a enteros, truncando la parte frac-
cionaria.
- Un double pas a a float, redondeando y perdiendo precisi6n si
el valor double no puede ser representado exactamente como
float.
• Tambien ocurre conversi6n cuando un valor es pasado como argu-
mento a una funci6n. Estas conversiones son ejecutadas indepen-
dientemente sobre cad a argumento en la Hamada. En general, esto
significa que un valor float es convertido a double, un valor chal
o short es convertido a int y un valor unsigned char 0 unsigned shorl
es convertido a unsigned into
En C, esta permitida una conversion explicita del tipo de una expresi6n
mediante una construcci6n denominada cast, que tiene la forma:
La expresi6n es convertida al tipo especificado aplicando las reglas de
conversi6n expuestas anteriormente.
Por ejemplo, la funci6n raiz cuadrada (sqrt), espera como argumento
un tipo double. Para evitar resultados inesperados en el caso de pasar un
argumento de otro tipo, podemos escribir:
Una variable de un determinado tipo, no siempre puede ser converti-
da explicitamente a otro tipo. Por ejemplo:
unsigned int a : 3;
unsigned int b : 1;
unsigned int c : 3;
unsigned int d : 1;
atributo;
II bits 0 a 2
II bit 3
II bits 4 a 6
II bit 7
La variable atributo es una estructura de longitud ocho bits. Si desea-
mos copiar atributo en una variable atrib de tipo char, seguramente escri-
biriamos:
char atrib;
atrib = (char}atributo;
10 cual da lugar a un error, ya que en general C no permite convertir una
estructura a un tipo como char, aunque como en este caso, la longitudes
de ambos tipos sean iguales.
Utilizando conversiones explicitas de tipo sobre punteros, es posible
convertir el valor de una variable de un determinado tipo a otro tipo cual-
quiera. El formate general para llevar esto a la pnictica es:
char *atrib;
atrib = (char *}&atributo;
define la variable a de tipo char cuyo contenido es el mismo que el de la
estructura atributo.
Algunas de las rutinas de las librerias de C, utilizan valores cuyos tipos
son definidos en los ficheros .h. Algunos de estos tipos y sus definiciones,
son los siguientes:
c1ock_t este tipo esta definido en time.h y es utilizado por la fundon
clock( ).
este tipo esta definido en stdio.h y es utilizado por las funcio-
nes jgetpos( ) y jsetpos( ).
este tipo esta definido en stdio.h y en otros ficheros .h. Es un
tipo entero sin signo, resultado del operador sizeo!
este tipo esta definido en time.h y es utilizado por la funci6n
timer ).
FILE el tipo estructura FILE esta definido en stdio.h y es utilizado
por las funciones estandar de entrada/salida.
Un programa fuente C es una colecci6n de cualquier numero de directrices
para el compilador, declaraciones, definiciones, expresiones, sentencias y
funciones.
Todo programa C debe contener una funci6n nombrada main(), don-
de el programa comienza a ejecutarse. Las llaves ({ J) que incluyen el cuer-
po de esta funci6n principal, definen el principio y el final del programa.
Un programa C, ademas de la funci6n principal main(), consta gene-
ralmente de otras funciones que definen rutinas con una funci6n especifi-
ca en el programa. Esto quiere decir que la soluci6n de cualquier proble-
ma, no debe considerarse inmediatamente en terminos de sentencias
correspondientes a un lenguaje, sino de elementos naturales del problema
mismo, abstraidos de alguna manera, que daran lugar al desarrollo de las
funciones mencionadas.
El disefio Top Down de programas, consiste precisamente en encon-
trar la soluci6n de un problema mediante la aplicaci6n sistematica de des-
composici6n del problema en subproblemas cada vez mas simples, aplicando
la maxima de dividir para veneer.
El empleo de esta tecnica de desarrollo de programas, as! como la uti-
lizaci6n unicamente de estructuras secuenciales, alternativas y repetitivas,
nos conduce a la denominada PROGRAMACION ESTRUCTURADA.
Todos los ejercicios de esta obra senin desarrollados bajo el concepto
de PROGRAMACION ESTRUCTURADA.
Este ejemplo presenta una tabla de equivalencia entre grados Centl-
grados y Fahrenheit de la forma siguiente:
-30 C
-24 C
-22.00 F
-11.20 F
90 C
96 C
194.00 F
204.80 F
1* Paso de grados Centigrados a Fahrenheit (F=915*C+32)
* Directrices para el preprocesador (#)
*1
# include Hstdio.h" 1* fichero estdndar de c: que se incluye en
* el program a
*1
I * Definicion de constantes *1
#define INF -30 1* limite inferior de la tabla de temperaturas *1
#define SUP 100 1* limite superior *1
1* Declaracion de funciones
* (funcion prototipo 0 declaracion forward)
*1
main( ) / *Juncion principal - comienza el programa */
[
/ *Declaracion de variables locales */
as,
!>A.
int centigrados;
int incremento = 6; / * deJinicion e inicializacion */
centigrados = INF; / * sentencia de asignacion */
while (centigrados < = SUP)
{
/ * Se llama a la Juncion y se Ie pasa un valor */
Jahrenheit = conversion(centigrados);
printf("%10d C %10.2J F n': centigrados, Jahrenheit);
centigrados + = incremento;
}
} / *Jin de la Juncion principal y del programa */
float conversion(int cent)
{
float Jhar; / * variable local conocida solamente aqul,
en la Juncion */
/ * los operandos son convertidos al tipo del operando de
precision mas alta (float: 9.0 0 5.0) */
Jhar = 9.0 / 5.0 * cent + 32;
return (fhar); / * retorna un valor a la sentencia de llamada */
} / * Fin de la Juncion de conversion */
La directriz # include "Jichero" Ie dice al compilador que incluya el fiche-
ro especificado, en el programa fuente. Esto es necesario porque estos fi-
cheros aportan, entre otras declaraciones, Ias funciones prototipo de Ias
funciones de Ia Iibreria estandar que utilizamos en nuestros programas.
Mediante Ia directriz # define identijicador valor se Ie indica al compila-
dor, que toda aparici6n en el programa de identijicador, debe ser sustitui-
da por valor.
#include "stdio.h" h jichero estdndar de c; que se incluye e.n
* el program a
*/
/ * Dejinicion de constantes */
#dejine INF -30 h limite injerior de la tabla de temperaturas d
#dejine SUP 100 / * limite superior */
Una declaraci6n introduce uno 0 mas nombres en el programa. Una decla-
raci6n es una definici6n excepto: cuando declara una funci6n sin especifi-
car el cuerpo de la misma, cuando contiene el calificador extern y no hay
inicializaci6n, cuando la declaraci6n corresponde a un nombre de una es-
tructura, 0 cuando es una declaraci6n typedej
iot jx( iut x )
{
return (x+ b);
J
struct complejo
{
float a, b;
J
extern int a;
extern const b;
int jx( int x );
struct complejo;
typedef int ENTERO;
Toda variable debe ser declarada antes de ser utilizada. En general,
las variables no son inicializadas por C, pero si se desea, pueden ser inicia-
lizadas en la propia declaracion.
La definicion de una variable, declara la variable y ademas Ie asigna
memoria; la definicion de una funcion, declara la funcion y ademas inclu-
ye el cuerpo de la misma.
int centigrados;
int incremento = 6;
float conversion (int cent)
I
float jahr;
jahr = 9.0 / 5.0 * cent + 32;
return (fahr);
J
La declaracion 0 la definicion de una variable, as! como la declara-
cion de una funcion, pueden realizarse a nivel interno (dentro de la defini-
cion de una funcion) 0 a nivel externo (fuera de toda definicion de fun-
cion). La definicion de una funcion, siempre ocune a nivel externo.
En el programa anterior, podemos observar las siguientes declaracio-
nes y definiciones:
/ * declaracion de una juncion
a nivel externo */
j * definicion a nivel externo */
/ * definicion a nivel intern0 d
float jahrenheit;
float jahr;
Una expresi6n es una combinaci6n de operadores y operandos que dan lu-
gar a un unico valor.
Una sentencia es la unidad ejecutable mas pequefia de un programa C. Las
sentencias controlan el flujo u orden de ejecuci6n. Una sentencia C consta
de una palabra clave (for, while, if ...else, etc.), expresiones, declaraciones,
o llamadas a funciones.
Dos 0 mas sentencias pueden aparecer sobre una misma linea, separa-
das por punta y coma.
Una sentencia compuesta 0 bloque, es una colecci6n de sentencias inclui-
das entre Haves ({ D. Un bloque puede contener otros bloques.
{
jahrenheit = conversion(centigrados);
printf(C<%10d C %10.2j F n': centigrados, jahrenheit);
centigrados + = incremento;
J
Una funci6n es una colecci6n de sentencias que ejecutan una tarea especi-
fica. Una funci6n no puede contener a otra funci6n.
Puesto que las funciones son una herramienta muy valiosa en la cons-
trucci6n de un programa C, vamos a anticiparnos a describir c6mo se de-
claran y se definen, con el fin de poder utilizarlas desde el primer momen-
ta en la programaci6n. Posteriormente se estudianin con mas detalle.
La declaraci6n de una funci6n, tambien conocida como funcion prototi-
po, consiste en:
Se observa, que en la declarad6n de la fund6n, se dan sus caracteris-
ticas pero no se define su contenido. Una fund6n puede ser declarada im-
plicitamente 0 con una declaracion forward (fund6n prototipo).
La declaracion implicita se da cuando la fund6n es llamada y no exis-
te una declarad6n previa (declaraci6n forward). En este caso, C, por de-
fecto, construye una fund6n prototipo con tipo de resuItado int y la lista
de tipos de argumentos se construye, en base a los parametros formales
espedficados en la definici6n de la fund6n. Esto obliga a que el tipo del
resultado en la definici6n de la fund6n sea into
La declaracion explicita, permite conocer las caracteristicas de la fun-
ci6n antes de ser utilizada.
La Iista de tipos de argumentos normal mente consiste en una lista de
identificadores con sus tipos, separados par comas. En el caso de una fun-
ci6n prototipo, se pueden omitir los identificadores, poniendo solamente
los tipos. El ambito de validez de estos argumentos, queda restringido a
la propia declaraci6n.
float juncion--:x(int a, float b, char c);
jloat juncion--:x(int, float, char);
Para asegurar la portabilidad, se puede utilizar el tipo void para espe-
cificar que una funci6n no acepta argumentos.
float juncion_a(void);
float juncion_a( );
Las funciones prototipo de las funciones pertenecientes a las librerfa~
estandar de C, como printf( ), son provistas por los ficheros de cabecera
estandar (ficheros .h).
La definici6n de una funci6n consta de una cabecera de funcion y del cuerpo
de la fundon encerrado entre Haves.
tipo-resultado nombre-funcion ([parametros jormalesJ)
[
declaraciones de variables locales;
sentencias;
[return( expresion)J;
1
Las variables locales declaradas dentro del cuerpo de la funci6n, por
definici6n solamente pueden utilizarse dentro del mismo.
El tipo del resultado especifica que tipo de datos retorna la funci6n.
Este, puede ser cualquier tipo fundamental, 0 tipo definido por el usuario,
pero no puede ser un array 0 una funci6n. Por defecto, el valor retornado
es into Este valor es devuelto a la sentencia de Hamada, por medio de la
sentencia:
Esta sentencia puede ser 0 no la ultima, y puede aparecer mas de una
vez en el cuerpo de la funci6n. En el caso de que la funci6n no retorne
un valor, se omite.
Los panimetros formales de una funci6n son las variables que reciben
los valores de los argumentos en la Hamada a la funci6n; consisten en una
lista de identificadores con sus tipos, separados por comas.
Para ejecutar una funci6n, hay que Hamarla. La Hamada a una funcion
consta del nombre de la misma y de una lista de argumentos 0 valores a
pasar denominados panimetros actuales, separados por comas yencerra-
dos entre parentesis.
/ * Se llama a la funci6n pasando el valor cent(grados */
jahrenheit = conversion(centigrados);
float conversion (int cent) / * cabecera de funci6n */
!
float fahr;
jahr = 9.0 / 5.0 * cent + 32;
return (fahr);
l /*fin de la funci6n de conversi6n */
La cabecera de la funci6n tambien se podia haber escrito utilizando
el estilo antiguo, de la siguiente forma:
float conversion (cent) / * cabecera de fundon */
int cent;
{
Cuando se Hama a una funcion, el valor del primer panimetro actual es
pasado al primer panimetro formal, el valor del segundo panimetro actual
es pasado al segundo panimetro formal y as! sucesivamente. Todos los ar-
gumentos, excepto los arrays, son pasados por valor. Esto es, a la funcion
se pasa una copia del argumento, no su direccion. Esto hace que la fun-
cion C, no pueda alterar los contenidos de las variables pasadas.
En el ejemplo anterior, cuando la funcion conversion es Hamada, el
panimetro formal cent recibe el valor del panimetro actual centigrados.
Si se desea poder alterar los contenidos de los argumentos en la Ha-
mada, entonces hay que pasarlos por referencia. Esto es, a la funcion, se
pasa la direccion del argumento y no su valor por 10 que el panimetro for-
mal correspondiente tiene que ser un puntero. Para pasar la direccion de
un argumento, utilizaremos el operador &.
main( )
{
int a = 20, b = 30;
intercambio(&a, &b); / * a y b son pasados por referenda */
printj(Ha es %d y b es %d n': a, b);
l
void intercambio(int 'i<X, int *y)
[
*Y;
z;
/ * contenido de x
/ * contenido de y
contenido de y */
z */
En este ejemplo observamos que la funci6n intercambio tiene dos pa-
rametros, x e y, de tipo "puntero a un entero", que reciben las direcciones
de a y b respectivamente. Esto quiere decir que, al modificar el contenido
de las direcciones x e y, indirectamente estamos modificando los valores
de a y b respectivamente. Recordar la definici6n de puntero y las definicio-
nes de los operadores de indireccion (*) y direccion-de (&).
Un programa C puede ser dividido en uno 0 mas ficheros fuente. Un fiche-
ro fuente C, es un fichero de texto que contiene todo 0 parte de un progra-
ma C.
Para compilar un programa formado por varios ficheros, se deben com-
pilar por separado cada uno de los ficheros y, a continuaci6n, enlazarlos
para formar un unico m6dulo ejecutable.
Un fichero fuente puede contener cualquier combinaci6n de directri-
ces para el compilador, deciaraciones y definiciones. Pero, un elemento como
una funci6n 0 una estructura, no puede ser dividido entre dos ficheros fuen-
tes. Por otra parte, un fichero fuente no necesita contener sentencias ejecu-
tables; esto es, un fichero fuente puede estar formado, por ejemplo, sola-
mente por definiciones de variables que son referenciadas desde otros
ficheros fuentes.
El siguiente programa C, nos da como resultado el mayor de tres va-
lores dados.
Este programa esta formado por dos ficheros independientes, deno-
minados progOl.c y prog02.c.
/************************* PROGOl.C *************************
Fichero juente I - junci6n principal
**************************************************************/
#dejine a 12
#dejine b 25
#dejine c 3
/ * Funci6n max. Toma dos valores, pI y p2, y una rejerencia p3 */
extern iut max(iut pI, iut p2, iut *p3);
main ( ) / *junci6n principal */
(
iut w = a, x = b, y = c;
iut Z = 0;
max(w, x, &z); / * z igual al mayor de w y x */
max(z, y, &z); / * z igual al mayor de z (anterior) e y d
printj("  nmayor = %d  n': z);
}
/ ************************* PROG02.C *************************
Fichero juente 2 - junci6n max
**************************************************************/
/ * Funci6n max. Toma dos valores, pI y p2, y una rejerencia p3 */
iut max(iut pI, iut p2, iut *p3)
{
if (pI < p2)
*p3 = pI;
else
*p3 = p2;
/ * el contenido de la direcci6n p3 es modijicado */
/ * con el valor de pI */
/ * el contenido de la direcci6n p3 es modijicado */
/ * con el valor de p2 */
Para compilar y enlazar los modulos progOl.c y prog02.c, utilizar la
siguiente orden:
Se denomina ambito de una variable (scope) a la parte de un programa
donde dicha variable puede ser referenciada por su nombre. Una variable
puede ser limitada a un bloque, a un fichero, a una funci6n, 0 a una decla-
raci6n de una funci6n prototipo.
Hay cuatro clases de ambito: local, global 0 fichero, funci6n y es-
tructura.
Cuando una variable se declara fuera de todo bloque en un programa, es
accesible desde su punta de definici6n 0 declaraci6n hasta el final del fi-
chero fuente. Esta variable recibe el calificativo de global.
Una variable global existe y tiene valor desde el principio hasta el fi-
nal de la ejecuci6n del programa. Todas las funciones tienen caracter glo-
bal. Un elemento con caracter global puede no ser accesible desde todas
las partes del programa.
Si la declaraci6n de una variable se hace dentro de un bloque, el acce-
so a dicha variable queda limitado a ese bloque y a los bloques contenidos
dentro de este por debajo de su punta de declaraci6n. En este caso, la va-
riable recibe el calificativo de local 0 automatica.
Una variable local existe y tiene valor desde su punta de declaraci6n
hasta el final del bloque donde esta definida. Cad a vez que el control se
pasa al bloque para su ejecuci6n, las variables son nuevamente definidas;
y cuando finaliza la ejecuci6n del mismo, las variables dejan de existir. Un
elemento con caracter local es accesible solamente dentro del bloque al que
pertenece.
EI siguiente ejemplo muestra el ambito de las variables, dependiendo de
si estan definidas en un bloque 0 fuera de todo bloque.
En este ejemplo, al tratar de definir el ambito de una variable, distin-
guimos cuatro niveles:
Las variables definidas en este nivel, son accesibles desde el punta
de definicion hasta el final del programa.
/ * Definici6n de var] como variable GLOBAL */
int var] = 50;
/ * Definici6n de var] y var2 como variables
LOCALES en BLOQUE ] Y BLOQUE 2 */
printjt'%d %d  n': var], var2);
/ * escribe 100 y 200 */
( / * COMIENZO DEL BLOQUE 2 */
/ * Redefinici6n de la variable LOCAL var] */
int var] = 0;
printj("%d %d  n': var], var2);
/ * escribe 0 y 200 */
l /* FINAL DEL BLOQUE 2 */
printf("%d  n': var]); h escribe ]00 */
l /* FINAL DEL BLOQUE ] */
printf("%d  n': var]); / * escribe 50 */
l /* FINAL DE main( ) Y DEL PROGRAMA */
Las variables definidas en este nivel, solamente son accesibles des-
de la propia funci6n main( ) y, por 10tanto, son accesibles en los
bloques 1 y 2.
Las variables definidas en este nivel, solamente son accesibles en
el interior del bloque 1 y, por 10 tanto, en el bloque 2.
Las variables definidas en este nivel, solamente son accesibles en
el interior del bloque 2.
En el ejemplo anterior se observa que una variable global y otra local
pueden tener el mismo nombre, pero no guardan relaci6n una con otra,
10cual da lugar a que la variable global quede anulada en el ambito de
accesibilidad de la local del mismo nombre. Como ejemplo observar 10que
ocurre en el programa anterior con varl.
Los panimetros formales declarados en la lista de parametros de la
definici6n de una funci6n, son locales a la funci6n; y 10s parametros de-
clarados en la lista de parametros de la declaraci6n de una funci6n proto-
tipo, tienen un ambito restringido a la propia declaraci6n de la funci6n.
El nombre de un campo 0 miembro de una estructura es local a la
misma y puede ser utilizado solamente: despues del operador "." aplicado
a una variable del tipo de esa estructura, 0 despues del operador "- >"
aplicado a un puntero a esa estructura. En los capitulos sucesivos, al tratar
las estructuras y punteros a estructuras, veremos esto con mas detalle.
Por defecto, todas las variables llevan asociada una clase de almacenamiento
que determina su ac.cesibilidad y existencia. Los conceptos de accesibili-
dad y de existencia tanto para variables como para funciones, pueden alte-
rarse por los calificadores:
auto
register
static
extern
almacenamiento automatico
almacenamiento en un registro
almacenamiento estatico
almacenamiento externo
Los calificadores auto 0 register pueden ser utilizados solamente con
variables locales; el calificador extern puede ser utilizado solamente con
variables globales 0 funciones; y el calificador static puede ser utilizado
con variables locales, globales 0 fund ones.
En una variable dec1arada a nivel externo, esto es, fuera de toda definicion
de fundon, se pueden utilizar los calificadores static 0 extern 0 bien omitir
el calificador. A nivel externo no se pueden utilizar los calificadores auto
o register.
Una variable dec1arada a nivel externo es una definicion de la variable
o una referencia a una variable definida en otra parte. Esto quiere decir
que la dec1aracion de una variable externa inicializa la variable acero, por
defecto, 0 a un valor especificado.
Una variable definida a nivel externo, puede ser accesible antes de su
definicion, 0 en otro fichero, utilizando el calificador extern.
Esto quiere decir, que la utilizacion del calificador extern tiene senti-
do cuando la variable ha side definida a nivel externo, una vez, y solamen-
.te una, en cualquier parte del programa y queremos tener acceso a ella en
otra parte donde no es visible.
/ * referencia a la variable var
definida a continuaci6n */
maine )
!
var+ +;
printf("%d  n'; var);
funcion~ ( );
I
funcion~( )
!
var+ +;
printf("%d  n'; var);
funcion~( );
I
/ * rejerencia a la variable var
dejinida en el jichero uno */
funcion~( )
!
var+ +;
printf("%d  n'; var);
I
3. La declaracion extern en el primer fichera, permite acceder a la
variable var, antes de su definicion. Sin la declaracion extern, la
variable global var no seria accesible en la funcion maine ).
4. La declaraci6n extern en el segundo fichero, permite acceder a la
variable var en este fichero.
5. Si la variable var no hubiera sido inicializada explfcitamente, C
Ie asignaria automaticamente el valor O.
Si se utiliza el calificador static en la declaraci6n de una variable a
nivel externo, esta solamente es accesible dentro de su propio fichero fuen-
te. Esto permite declarar otras variables static con el mismo nombre en otros
ficheros correspondientes al mismo programa.
En una variable declarada a nivel interno, esto es, dentro de un bloque,
se pueden utilizar cualquiera de los cuatro calificadores, u omitir el califi-
cador, en cuyo caso se considera la variable como auto (local 0 automatica).
Una variable declarada como auto solamente es visible dentro del blo-
que donde esta definida. Este tipo de variables no son inicializadas auto-
maticamente, por 10 que hay que inicializarlas explfcitamente, cuando sea
necesario.
Una variable declarada a nivel interno como static, solamente es visi-
ble dentro del bloque donde esta definida; pero, a diferencia de las auto-
maticas, su existencia es permanente, en lugar de aparecer y desaparecer
al iniciar y finalizar la ejecuci6n del bloque que la contiene.
Una variable declarada static es inicializada solamente una vez, cuan-
do comienza la ejecuci6n del programa. No es reinicializada cada vez que
se ejecuta el bloque que la contiene. Si la variable no es inicializada expli-
citamente, C la inicializa automaticamente a O.
Una declaraci6n register indica al compilador que la variable sera al-
macenada, si es posible, en un registro de la maquina, 10 que producini
programas mas cortos y mas rapidos. EI numero de registros utilizables para
este tipo de variables, depende de la maquina. Si no es posible almacenar
una variable register en un registro, se la da el tratamiento de automatica.
Este tipo de declaraci6n es valida para variables de tipo int y de tipo pun-
tero, debido al tamafio del registro.
Una variable declarada como register solamente es visible dentro del
bloque donde esta definida. Este tipo de variables no son inicializadas auto-
maticamente, por 10 que hay que inicializarlas explicitamente, si es necesario.
Una variable declarada extern, referencia a una variable definida con
el mismo nombre a nivel externo en cualquier parte del programa. La de-
claracion extern a nivel interno es utilizada para hacer accesible una varia-
ble externa, en una funcion 0 modulo en el cual no 10 es.
main( )
[
/ * se hace referenda a la variable varl */
extern int varl;
/ * var2 es accesible solamente dentro de main.
Su valor inidal es O. */
static int var2;
/ * var3 es almacenada en un registro, si es posible */
register int var3 = 0;
/ * var4 es declarada auto, por defecto */
int var4. = 0;
varl + = 2;
/ * se escribefi los valores 7, 0, 0, 0 */
printft(%d %d %d %d  n': varl, var2, var3, var4);
juncion---l( );
I
juncion---l( )
[
/ * se define la variable local varl */
int varl = 15;
/ * var2 es accesible solamente dentro de fundon---l */
static var2 = 5;
var2 + = 5;
/ * se escriben los valores ]5, ]0 */
printf((%d %d  n': var], var2);
J
En este ejemplo, la variable var] es definida a nivel externo. En la fun-
cion main( ) se utiliza una declaracion extern, para hacer accesible dentro
de esta, la variable var]. La variable var2 declarada static es inicializada,
por defecto, a O.
En la funcion denominadafuncion~ se define la variable local varl,
anulando asi a la variable €xterna var]. La variable var2, declarada static,
es inicializada a 5. Esta definicion no entra en conflicto con la variable var2
de la funcion main( ), ya que las variables static a nivel interno son visibles
solamente dentro del bloque donde estan declaradas. A continuacion la
variable var2 es incrementada en 5, de tal forma que sifuncion~ fuera
Hamada otra vez, el valor inicial para esta variable seria de 10, ya que las
variables intern as declaradas static, conservan sus valores de una ejecucion
a otra del bloque.
Una fundon declarada static es accesible solamente dentro del fichero fuente
en el que esta definida.
Una funcion declarada extern es accesible desde todos los ficheros fuen-
tes que componen un programa.
Para presentar los formatos de las sentencias, macros y funciones de C,
se aplicaran las mismas reglas enunciadas al principio del capitulo 2.
Cuando se trate de presentar la sintaxis correspondiente a una macro
o a una funcion, se dara la siguiente informacion:
1. Fichero con extension .h (# include < fichero.h » que contiene
las definiciones y/o declaraciones con respecto a esa funcion y
afines.
2. Funcion prototipo para indicar el tipo del resultado y la lista de
argumentos.
funcion prototipo y
definicion de cada argumento
total = 0;
area = 3.141592 * r * r;
cuenta + = 1;
La sentencia de asignacion es asimetrica. Esto quiere decir que la ex-
presion de la derecha es evaluada, y el resultado es asignado a la variable
especificada a la izquierda. De acuerdo con esta definicion, no serfa valida
la sentencia:
Si la variable es de tipo puntero, solamente se la puede asignar una
direccion de memoria, la cual sera siempre distinta de O. Un valor 0 (se
escribeNULL) sirve para indicar que esa variable puntero no apunta a un
dato valido.
iut a = 10, *P;
p = &a; / * se asigna a p fa direcci6n de a */
Las operaciones de entrada y salida no forman parte del conjunto de sen-
tencias de C, sino que pertenecen al conjunto de funciones de la libreria
estandar de C. Por ello, todo fichero fuente que utilice funciones de entra-
da/salida correspondientes a la libreria estandar de C, necesita de las fun-
ciones prototipo correspondientes a estas, par 10 que deb era contener la linea:
Las dobles comillas significan que el fiehero especificado, debe ser bus-
cado en el directorio actual de trabajo y si no se encuentra, la busqueda
debe continuar en el directorio estandar para los ficheros con extensi6n .h
(directorio include).
Si el fichero especificado, en lugar de escribirlo entre comillas, 10 es-
cribimos entre angulos:
la busqueda de dicho fichero se efectua solamente en el directorio estan-
dar para los ficheros con extensi6n .h (directorio include).
La funci6n printf( ) escribe con formato, una serie de caracteres, 0 un va-
lor, en el fichero de salida estandar stdout. Esta funci6n devuelve un valor
entero igual al numero de caracteres escritos.
especifica como va a ser la salida. Esta formado por caracteres
ordinarios, secuencias de escape y especificaciones de forma-
to. El formato se lee de izquierda a derecha. Cada argumento
debe tener su correspondiente especificaci6n y en el mismo or-
den. Si hay mas argumentos que especificaciones de formato,
los argumentos en exceso se ignoran.
justifica el resultado a la izquierda, dentro del ancho especi-
ficado. Por defecto la justificaci6n se hace a la derecha.
antepone el signo + (mas) 0 - (menos) al valor de salida.
Por defecto solo se pone signo - a los valores negativos.
rellena con ceros no significativos hasta alcanzar el ancho
minimo.
antepone un blanco al valor de salida sies positivo. Si se uti-
liza junto con + entonces se ignora.
cuando se utiliza con la especificaci6n de formate 0, x, 0 X,
antepone al valor de salida 0, Ox, 0 OX respectivamente.
Cuando se utiliza con la especificaci6n de formate e, E, 0
f, fuerza a que el valor de salida contenga un punta decimal
en todos los casos.
Cuando se utiliza con la especificaci6n de formate g, 0 G,
fuerza a que el valor de salida contenga un punta decimal
en todos los casos y evita que los ceros arrastrados sean
truncados.
minimo numero de posiciones para la salida. Si el valor a
escribir ocupa mas posiciones de las especificadas, el ancho
es incrementado en 10 necesario.
Si el ancho y/o la precision se especifican con el caracter *,
el valor para estos campos se toma del siguiente argumento
entero.
int ancho = 15, precision =2;
float valor= 12.345;
printj((% *. 4'~ancho, precision, valor);
(double) valor con signo de la forma: [-]dddd.dddd. El nu-
mero de digitos antes del punta decimal depende de la mag-
nitud del numero y de la cantidad de decimales de la preci-
sion, la cual es 6 por defecto.
(double) valor con signo, en formato foe (el que sea mas
compacta para el valor y precisi6n dados).
(double) igual que g, excepto que G introduce el exponente
E en vez de e.
(int) un solo caracter, correspondiente al byte menos signifi-
cativo.
(cadena de caracteres) escribir una cadena de caracteres has-
ta el primer caracter nulo ('  0').
(puntero a un entero). En el entero es almacenado el numero
de caracteres hasta ahora escritos en el buffer.
(puntero a void). Escribe la direcci6n apuntada por el argu-
mento. Si se especifica 070p0 %Np se escribe solamente el
offset de la direcci6n y si se especifica %Fp 0 %Ip se escribe
una direcci6n segmentada (xxxx:yyyy). En este ultimo caso
se espera un puntero a un valor far por ello bajo el modelo
small, utilizar con el argumento a escribir, la construcci6n
cast: (tipo far *)arg.
la precisi6n especifica el minima numero' de digitos que se
tienen que escribir. Si es necesario se rellena con ceros a la
izquierda. Si el valor excede de la precisi6n, no se trunca.
e,E,f 1a precisi6n especifica e1numero de digitos que tienen que
ser escritos despues del punto decimal. E1valor es redondeado.
Por defecto es 6.
g,G 1aprecisi6n especifica e1maximo numero de digitos signifi-
cativos (6 por defecto) que se tienen que escribir.
s 1aprecisi6n especifica e1maximo numero de caracteres a ser
escritos. Los caracteres que excedan este numero, se ignoran.
F y N son una amp1iaci6n de Microsoft C, por 10 que no
pertenecen a1 C estandar.
se utiliza como prefijo con 10stipos d, i, 0, x, y X, para es-
pecificar que e1argumento es short int, 0 con u para especi-
ficar un short unsigned into
se utiliza como prefijo con 10stipos d, i, 0, x, y X, para es-
pecificar que e1argumento es long int, 0 con u para especifi-
car un long unsigned intoTambien se utiliza con 10stipos e,
E, f, g, y G para especificar un double en 1ugar de un float.
se uti1iza como prefijo con 10stipos e, E, f, g, y G, para es-
pecificar long double.
# include <stdio.h>
# include <stdlib.h>
main( )
[
char car;
static char nombre[ J = C<Latemperatura ambiente";
int a, b, c;
float x, y, z;
car = 'C'; a = 20,' b = 350; c = 1991;
x = 34.5; y = 1234; z = 1.248;
system(C<c!s"); / * limpiar la pantalla */
printf(C< n%s es de ': nombre);
printj(C<%d grados %c  n': a, car);
printj(C< n ");
printj(C<a = %6d tb = %6d tc = %6d n': a, b, c);
printf(C< nLos resultados son los siguientes:  n ");
printj(C< n%5s  t  t%5s  t  t%5s  n': C<x': 'Y: c<z");
printj(C<  n");
printj(C< n%8.21 t%8.21 t%8.21': x, y, z);
printj(C< n%8.21 t%8.21 t%8.21 n': x+y, y/5, z*2);
printj(C< n  n ");
z *= (x + y);
print1t'Valor resultante: %.31 n': z);
J
34.50
1268.50/
1234.00
246.80
1.25
2.50
main( )
!
int i = 10, a = 12345;
printjt'%d %n  n': a, &i); / * hasta ahora hay en el buffer
12345bb (b = blanco) */
printj("%d  n': i);
printj("  n%10s n%10s n': "abc': tiabcdef");
Crintjt'  n%-10s n%-10s n': "abc': "abcdef");
1
Resultado:
12345
7
abc
abcdef
abc
abcdef
La funci6n scanf( ) lee datos de la entrada estandar stdin, los interpreta
de acuerdo con el formato indicado y los almacena en los argumentos es-
pecificados. Cada argumento debe ser un puntero a una variable cuyo tipo
debe corresponderse con el tipo especificado en el formato. Esta funci6n
devuelve un entero correspondiente al numero de datos leidos y asignados
de la entrada. Si este valor es cero, significa que no han sido asignados
datos. Cuando se intenta leer un end-of-file (marca de fin de fichero) la
funci6n scanf( ) retorna un EOp, constante definida en el fichero stdio.h.
interpreta cada dato de entrada. Esta formado por caracteres
en blanco «:  t,  n), caracteres ordinarios yespecificacio-
nes de formato. El formato se lee de izquierda a derecha.
Cada argumento debe tener su correspondiente especificaci6n
de forma.to y en el mismo orden.
Si un caracter en la entrada estandar no se corresponde con
la entrada especificada por el formato, se interrumpe la en-
trada de datos.
argumento es un puntero a la variable que se quiere leer.
~ando se especifica mas de un argumento, los valores correspondientes
en la entrada hay que separarlos por uno 0 mas espacios en blanco « :
 t,  n) 0 por el caracter que se especifique en el formato.
Un espacio en blanco antes 0 despues de una especificaci6n de forma-
to hace que scanf( ) lea, pero no almacene, todos los caracteres espacio en
blanco, hasta encontrar un caracter distinto de espacio en blanco.
scanf((%d %j %c': &a, &b, &c);
scanf((%d, %j, %c': &a, &b, &c);
scanf((%d : %j: %c': &a, &b, &c);
5 23.4 b
5, 23.4 , b
5:23.4 : b
Especificaciones de formato que no incluyan espacios en blanco como
separadores, no son aconsejables por ser muy rigidas en su uso.
scanf(H%d%f%c': &a, &b, &c);
scanf(H%d,%f,%c': &a, &b, &c);
5 23.4b
5,23.4,b
un aster isco a continuacion del simbolo 070 suprime la asigna-
cion del siguiente dato en la entrada.
maximo numero de caracteres a leer de la entrada. Los caracte-
res en exceso no son tenidos en cuenta.
F y N son una ampliacion de Microsoft C, por 10que no perte-
necen al C estandar.
h se utiliza como prefijo con 10stipos d, i, n, 0, y x, para especifi-
car que el argumento es short int, 0 con u para especificar un
short unsigned into
se utiliza como prefijo con 10stipos d, i, n, 0, y x, para especifi-
car que el argumento es long int, 0 con u para especificar un
long unsigned intoTambien se utiliza con los tipos e~f, y g para
especificar un double.
tipo el tipo determina si el dato de entrada es interpretado como un
canlcter, como una cadena de caracteres 0 como un numero. El
formate mas simple contiene el simbolo 070 y el tipo. Por ejem-
plo: %i.
el argum. es
car. un puntero a entrada esperada
u unsigned int
0 int
x~X int
f
e~E
g~G float
c char
s char
n int
enteros con signo en base 10, 16 u 8. Si el entero co-
mienza con 0 se toma el valor en octal y si empieza
con Ox 0 OX el valor se toma en hexadecimal.
en el entero es almacenado el numero de caracteres
leidos del buffer 0 del fichero. Por ejemplo:
long a; int r;
scanf("%ld%n'~ &a, &r);
printf("Caracteres lefdos: %ld  n'~ r);
el argum. es
car. un puntero a entrada esperada
p puntero a void lee una direcci6n y la almacena en el argumento. EI
dato leido es interpretado como un valor en hexade-
cimal. Por ejemplo:
int *a;
scanjt'%p': &a);
main( )
[
int a, r; float b; char c, s[20J;
printf("Introducir un valor entero, un real y un char  n = > ");
r = scanf("%d %j %c': &a, &b, &c);
printf("  nNumero de datos lefdos: %d  n': r);
printf("Datos lefdos: %d %j %c  n': a, b, c);
printf("  n  n");
printj("Valor hexadecimal: ");
scanf("%i': &a);
printf("Valor decimal: %i  n': a);
J
lntraducir un valor entera, un real y un char
= >12 3.5 x
Numera de datos leidos: 3
Datos leidos: 12 3.500000 x
Valor hexadecimal: OxAB
Valor decimal: 171
Con la especificaci6n de formato OJoc, se lee cualquier carclcter, inclu-
yendo los espacios en blanco (' "  t,  n).
Por ejemplo, el siguiente ejercicio lee caracteres de la entrada estan-
dary los escribe en la salida estandar. La entrada de datos finalizara cuan-
do pulsemos Ctrl+Z (end-of-file).
# include <stdio.h>
main( )
[
char car; int r;
r = scanf(C<%c': &car);
while (r != EOF)
[
printf(C<%c': car);
r = scanf(C<%c': &car); ~
I
l
Sabemos que la funci6n scanf( ) lee datos delimitados por espacios
en blanco. Pues bien, para leer cadenas de caracteres que contengan espa-
cios en blanco, tenemos que sustituir la especificaci6n de formato OJospor
O1o[A n], por ejemplo, que indica leer caracteres hasta encontrar un carac-
ter  n.
scanf(C<%[A nr: nombre);
printf("%s': nombre);
Entrada: Francisco Javier
Resultado: Francisco Javier
Observar que nombre no lleva el operador de direcci6n &, por tratarse
de un array. El identificador nombre es un puntero (direcci6n) a la cadena
de caracteres.
Si en lugar de especificar el formato %[A  nJ se hubiera especificado
el formato %s, el resultado hubiera sido: Francisco.
Un conjunto de caracteres entre [ ], como formato, indica leer carac-
teres hasta que se lea uno que no este especificado en el conjunto. El efecto
inverso se consigue anteponiendo al conjunto de caracteres el simbolo 
esto es, [Acaracteres).
Lee un canlcter de la entrada estandar stdin y avanza la posici6n de lectura
al siguiente caracter a leer.
Esta funci6n devuelve el caracter leido, 0 un EOF si se detecta el final
del fichero 0 si ocurre un error.
car = getchar( ); / * lee un cardcter y 10 almacena en
la variable car */
Escribe un caracter en la salida estandar stdout en la posici6n actual y avanza
a la siguiente posici6n de escritura.
putchar(car); / * escribe el cardcter contenido en la
variable car */
main( )
{
char car;
printf("Introducir texto. Finalizar con AZ  n");
while((car = getchar( )) != EOF)
putchar(car);
Cuando se estan introduciendo datos a traves del teclado y pulsamos la
tecla Enter (en otros ordenadores New Line, Return) se introduce el carac-
ter denominado fin de linea, cuya representaci6n en C se hace por medio
de la secuencia de escape  n.
Igualmente, el caracter fin de fichero, representado simb6licamente por
EOF, se obtiene bajo el sistema operativo UNIX pulsando las teclas Ctrl +D
y bajo el sistema operativo DOS pulsando las teclas Ctrl + Z. EOF es una
constante definida en el fichero stdio.h y tiene un valor de -1.
El siguiente programa lee, repetidamente, datos de la entrada estan-
dar hasta encontrar la marca de fin de fichero.
main( )
{
int a, r,
char c;
do
{
printf("a%d = '~ i);
r = scanf("%d'~ &a);
while ( r / = EOF)
{
if ( r )
i+ +;
else
jjlush(stdin);
printjt'a%cr-=-----'~ 0;
r = scanf("%d'~ &a);
J
printjt';,Desea jinalizar? sin: ");
clearerr(stdin);
c = getchar( );
J
while ( c /= 's');
J
Si introducimos un valor no valido para a[i], por ejemplo "a", no sera asig-
nado (r= 0) permaneciendo en el buffer asociado con la entrada estandar,
10 que d~.;ugar a un bucle infinito; jjlush(stdin) borra el contenido del buffer
asociado con la entrada estandar, 10 que permitira que scanj solicite un
nuevo dato de la entrada.
Cuando se teclea Ctrl +Z (0 Ctrl +D en UNIX) se activa el indicador de fin
de fichero asociado con stdin (r=EOF), y finaliza la repetitiva while; Mien-
tras este indicador no se desactive, cualquier funci6n que intente leer de la
entrada estandar, se encontrara con una condici6n de fin de fichero y devol-
vera un valor EOP. Por esta causa, en el ejemplo, getchar( ) no solicitara
un dato de la entrada estandar, 10 que nos conduce de nuevo a un bucle
infinito; clearerr(stdin) desactiva el indicador de fin de fichero de la entrada
estandar, 10 que permitira que getchar() solicite un nuevo dato de la entrada.
La funci6n getch( ) lee un canicter del teclado, sin visualizarlo; la funci6n
getche( ) lee un canicter del teclado visualizandolo.
~
int getche(void);
Ambas funciones leen un caracter de la memoria intermedia del tecla-
do. Cuando se ejecuta una funci6n de estas, la ejecuci6n se detiene hasta
que se pulse una tecla. No es necesario pulsar Enter. El resultado es un
byte cuando la tecla pulsada se corresponde con uno de los caracteres de
la tabla de C6digos de Caracteres de ASCII; y el resultado son dos bytes
cuando la tecla 0 combinaci6n de teclas pulsadas se corresponden con al-
guna de la tabla de los C6digos Extendidos; estas tablas se pueden ver en
los apendices. Para este ultimo caso, hay que Hamar a la funci6n dos ve-
ces, ya que es la segunda Hamada, la que proporciona el c6digo deseado
(segundo c6digo).
printf("pufse una tecla para continuar ");
getche( );
En este ejemplo, la ejecuci6n continuara despues de pulsar una tecla,
la cual sera visualizada.
EI siguiente ejemplo, almacena en la variable byte2, el c6digo extendi-
do de la tecla de funci6n, tecla de movimiento del cursor, combinaci6n de
teclas etc., que se pulse.
printj("pufse fa combinaci6n de teclas cuyo c6digo extendido 
desea conocer  n ");
byte] = getch( ); byte2 = getch( );
printf("%d  t %d'~ byte], byte2);
Fl
Alt+A
Shift+FlO
Ctrl+Home
flecha hacia arriba
~
59
30
93
119
72
Esta funci6n pasa la cadena de caracteres al interprete de 6rdenes del siste-
ma operativo, para ejecutar la orden indicada.
La cadena de caracteres representa una orden para MS-DOS. Si la ca-
dena de caracteres es nula, la funci6n simplemente comprueba si el inter-
prete de 6rdenes COMMAND.COM esta presente. Si la cadena de caracte-
res no es nula, la funci6n retorna un valor 0 si la orden es ejecutada, y un
valor distinto de cero si ocurre un error, como por ejemplo: no se encuen-
tra COMMAND.COM, la cadena excede de 128 bytes, 0 no hay suficiente
espacio de memoria para ejecutar la orden.
system("cls"); II limpiar fa pantalla
r == system("dir *.c");
I
Realizar un programa que de como resultado el interes producido )
el capital total acumulado de una cantidad c, invertida a un interes rOJo al ana.
# include "stdio.h"
# include "stdlib.h"
main( )
!
double c, intereses, capital;
float r;
/ * Entrada de datos */
printj("Capital invertido ");
scanf("%/f': &c);
printj("  nA un %% anual del ");
scanf("%f': &r); printf(" n  n  n");
/ * Cdlculos */
intereses = c * r / 100;
cJpital = c + intereses;
/ * Escribir resultados */
printf("Intereses producidos %10.0lf n': intereses);
printj("Capital acumulado %10.0lf n': capital);
1
Realizar un programa que de como resultado las soluciones reales xl
y x2 de una ecuaci6n de segundo grado, de la forma:
Las soluciones de una ecuaci6n de segundo grado vien en dad as por
la expresi6n:
-b ± sqrt(b2 - 4*a*c)
2*a
# include "stdio.hn
# i."1c1ude"stdlib.h n
# include "math.h n
main( )
( ---double a, b, c, d, xl, x2;
/ * Entrada de datos */
printf("Introducir coeficientes a b c: n);
scanf("%!j %!j %!j': &a, &b, &c);
/ * Cdlculo de las soluciones */
d = sqrt(b * b - 4 * a * c);
xl (-b + d) / (2 * a);
x2 = (-b - d) / (2 * a);
/ * Escribir resultados */
printf("EI valor de los coeficientes es: nn);
printf("a = %g  t b = %g  t c = %g  n': a, b ,c);
printft'  nSoluciones:  nn);
printf("xl = %g  nx2 = %g  n': xl, x2);
)
Ellector podrei comprobar que para algunos valores de a, bye se ob-
tiene un error. Eso es debido a que d toma un valor negativo, 10 que indica
que no hay raices reales sino complejas. Para dar soluci6n a este proble-
ma, ver las sentencias de control en el pr6ximo capitulo.
Toma una decision referente a la accion a ejecutar en un programa, basan-
dose en el resultado (verdadero 0 falso) de una expresion.
if(expresion)
sen ten cial;
[else
sentencia2] ;
expresion debe ser una expresion numerica, relacional 0 logica. El
resultado que se obtiene al evaluar la expresion es verda-
dero (no cero) 0 falso (cero).
sentencial12 representan una sentencia simple 0 compuesta. Cada sen-
tencia simple debe estar separada de la anterior por un pun-
to y coma.
1
Si el resultado de la expresion es verdadero, se ejecutani 10 indicado
por la sentencial.
Si el resultado de la expresion es falso, se ejecutani 10 indicado por
la sentencia2.
Si el resultado de la expresion es falso, y la clausula else se ha omitido,
la sentencia1 se ignora.
En cualquier caso, la ejecucion continua con la siguiente sentencia eje-
cutable.
if (x)
b=a/x;
b=b+1;
En este ejemplo la expresion es una expresion numeric a x. Entonces
b = a / x, que representa la sentencia1, se ejecutara si la expresion es ver-
dadera (x distinta de 0) y no se ejecutara si la expresion es falsa (x igual
a 0). En cualquier caso, se continua la ejecucion en la linea siguiente,
b=b+J.
En este otro ejemplo, la expresion a < b es una expresion de relacion.
La sentencia c = c + 1, solo se ejecutara si a es menor que b. Si a es mayor
o igual que b, se continua en la linea siguiente, ignorandose la sentencia
c=c+J.
if (a && b)
x = i;
En este ejemplo, la expresion a && b es una expresion logica. La sen-
tencia x = i solo se ejecuta si a y b son distintos de cero. En otro casa,
la sentencia x = i se ignora.
if (a = = b * 5)
{
x = 4;
a a+x;
}
else
b = 0;
En el ejemplo anterior, si se cumple la condici6n a = = b~, se ejecu-
tan las s~ntencias x = 4 y a = a + x. En otro caso, se ejecuta la sentencia
b = O. En ambos casos, la ejecuci6n continua en la siguiente linea de
programa.
if (car = = 's')
break;
La sentencia break-se-ejecutani solamente cuando car sea igual al ca-
racter 's:
Las sentencias if ...else pueden estar anidadas. Esto quiere decir que como
sentencial 0 sentencia2, de acuerdo con el formato, puede escribirse otra
sentencia if.
if (expresionl)
!
if (expresion2)
sentencial;
I
else
sentencia2;
if (expresionl)
if (expresion2)
sentencial;
else
sentencia2;
EI siguiente segmento de programa comprueba como es un numero
a con respecto a otro b.
if (a > b)
printj(H%d es mayor que %d': a, b);
else if (a < b)
printj(H%d es menor que %d': a, b);
else
printj(H%d es igual a %d': a, b);
Cuando en una linea de programa aparecen sentencias if ...else ani-
dadas, Ia regIa para diferenciar cada una de estas sentencias, es que cada
else se corresponde con el if mas proximo que no haya sido emparejado.
if (a = = b)
if (b = = e)
printj(Ha = b = e");
else
printj(Hb /= e");
En este ejemplo aparecen dos sentencias if anidadas. Aplicando Ia re-
gIa anterior, el else se corresponde con el segundo if.
if (a = = 0)
if (b /= 0)
s=s+b;
else
s=s+a;
En este otro ejemplo, cuando a / = 0 se pasa a ejecutar la siguiente
linea de programa. Si 10 que se desea es que se ejecute s = s + a cuando
a / = 0, entonces tendriamos que escribir:
if (a = = 0)
[
if (b /= 0)
s s+b;
---J
else
s=s+a;
Realizar un programa que de como resultado el menor de tres n4me-
ros a, b, c.
# include <stdio.h>
# include <stdlib.h>
main( )
[
float a, b, c, men or;
system("c!s");
printf(HNumeros a b c : ");
scanf(H%f %f %1': &a, &b, &c);
if (a < b)
if (a < c)
menor a;
else
menor =c;
else
if (b < c)
menor b;
else
printf(Hmenor
J
La estructura presentada a continuaci6n, aparece con bastante frecuencia
, y:es por 10 que Ie damos un tratamiento por separado. Esta estructura es
, ,consecuencia de las sentencias if anidadas .
.' . S:-':· ,:,: ..
(i~t2;,:;,< if (expresionl)
sentencial;
else if (expresion2)
sentencia2;
else if (expresion3)
sentencia3;
else
sentenciaN
Si se cumpIe la expresionl, se ejecuta la sentencial y si no se cumple
se examinan secuencialmente las expresiones siguientes hasta else, ejecu-
tandose la sentencia correspondiente al primer else if, cuya expresi6n sea
cierta. Si todas las expresiones son falsas, se ejecuta la sentenciaN corres-
pondiente a else. En cualquier caso, se continua en la sentencia que sigue
a la estructura.
Al efectuar una compra en un cierto almacen, si adquirimos mas de
100 unidades de un mismo articulo, nos hacen un descuento de un 40 0/0,
entre 25 y 100 un 20 070, entre 10 y 24 un 10 % y no hay descuento para
una adquisici6n de menos de 10 unidades. Calcular el importe a pagar.
# include <stdio.h>
# include <stdlib.h>
main( )
(
int ar, cc;
float pu;
systemt ecls");
printj("C6digo artlculo................. ");
scanf("%d': &ar);
printf("  nCantidad comprada....... ");
scanf("%d': &cc);
printf("  nPrecio unitario................. ");
scanf("%f': &pu);
printf("  n  n%10s %1Os %10s %1Os %10s  n  n':
"Artlculo': "Cantidad': "P. U:: "Dto:: "Total");
if (cc > 100)
printj(" %9d%% %10.2/ n': 40, cc * pu * 0.6);
else if (cc > = 25)
printf(" %9d%% %10.2/ n': 20, cc * pu * 0.8);
else if (cc > = 10)
printf(" %9d%% %10.2/ n': 10, cc * pu * 0.9);
else
printf(" %1Os %10.2/ n': "--': cc * pu);
Para poder imprimir un simbolo con un significado especial para C,
este tiene que ser duplicado en la expresi6n correspondiente. Como ejem-
plo, observar en el ejercicio anterior el formato %9d%%: %9d es el for-
mato utilizado para escribir el tanto por ciento de descuento y %% es para
escribir a continuaci6n el canicter "%':
Esta sentencia permite ejecutar una de varias acciones, en funci6n del va-
lor de una expresi6n.
[switch (expr-test)
{
[declaraciones]
case cte.l:
[sentencial;]
[case cte.2:]
[senten cia2;]
[case cte.3:]
[sentencia3;]
[default:]
[sentenciaJv,·]
cte.i e~una constante entera, una constante de un solo canicter 0 una
expresion constante; en todos los casos, el valor resultante tiene
que ser entero.
Al principio del cuerpo de la sentencia switch, pueden aparecer decla-
raciones. Las inicializaciones, si las hay, son ignoradas.
La sentencia switch evalua la expresion entre parentesis y compara su
valor con las constantes de cada case. La ejecucion de las sentencias del
cuerpo de la sentencia switch, comienza en el case cuya constante coincida
con el valor de la expr-test y continua hasta el final del cuerpo 0 hasta una
sentencia que transfiera el control fuera del cuerpo (por ejemplo break).
La sentencia switch puede incluir cualquier numero de c1<iusulascase.
Si no existe un valor igual al valor de la expr-test, entonces se ejecutan
las sentencias a continuacion de default, si esta c1<iusulaha side especifica-
da. La chiusula default puede colocarse en cualquier parte del cuerpo y no
necesariamente al final.
Leer una fecha representada por dos enteros, mes y ano y dar como
resuitado Ios rlias correspondientes al meso Tener en cuenta que Febrero puede
tener 28 0 29 dias si el ano es bisiesto. Un ano es bisiesto cuando es muIti-
pIa de 4 y no de 100 0 cuando es muitiplo de 400.
# include <stdia.h>
# include <stdlib.h >
main( )
[
unsigned iot dd, mm, aa;
system(Hcls");
printft'Inlraducir mes (# #) y ana (# # # #): ");
scanf(H%d %d': &mm, &aa);
switch (mm)
[
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
dd = 31;
break;
case 4:
case 6:
case 9:
case 11:
dd = 30;
break;
case 2:
if ((aa 0/'0 4 0) && (aa % 100 != 0) II (aa % 400 0))
dd = 29;
else
dd = 28;
break;
default:
printj("  nEI mes no es wi/ido  n ");
}
if (mm > = 1 && mm < = 12)
printf("  nEI mes %2d del ano %4d tiene %2d dfas  n':mm,aa,dd);
Esta sentencia finaliza la ejecuci6n de una sentencia do, for, switch, 0 while
en la cual aparece.
Cuando estas sentencias estan anidadas, la sentencia break solamente
finaliza la ejecuci6n de la sentencia donde esta incluida.
EI siguiente ejemplo calcula el importe a pagar por un vehiculo al cir-
cular por una autopista.
Se utiliza un tipo enumerado. Las variables de un tipo enumerado son
tratadas como si fueran de tipo into A cada elemento de un tipo ordinal
se Ie asigna el numero de orden partiendo del O.Este numero de orden puede
ser alterado, como se hace en el ejemplo.
# include <stdio.h>
# include <stdio.h>
main( )
{
enum tipo_vehiculo
{
bidcleta = 1,
moto,
coche,
camion
enum tipo_vehiculo vehiculo;
int km, tm, importe;
system("c!s");
printf("  t1 - bic!eta  n");
printf("  t2 - mota  n");
printf("  t3 - coche  n ");
printf("  t4 - camion  n");
printf("  n  tPulse la opci6n deseada ");
scanf("%d': &vehiculo);
switch (vehiculo)
{
case bicic!eta:
importe = 100;
break;
case mota:
case coche:
printj("  n;,Kil6metros? ");
scanf("%d': &km);
importe = 30 * km;
break;
case camion:
printf("  n;,Kil6metros y toneladas? ");
scanf("%d O/Od':&km, &tm);
importe = 30 * km + 25 * tm;
break;
default:
printf("  nLa opci6n no es correcta  n ");
exit(l); / * error; saIii'del program a */
I
printj("  nlmporte
I
Ejecuta una sentencia, simple 0 compuesta, cero 0 mas veces, dependien-
do del valor de una expresi6n.
while (expresi6n)
sentencia;
2. Si el resultado de la expresi6n es cero (falso), la sentencia no se
ejecuta y se pasa a ejecutar la siguiente sentencia en el programa.
3. Si el resultado de la expresi6n es distinto de cero (verdadero), se
ejecuta la sentencia y el proceso se repite comenzando en el punto 1.
La rutina siguiente solicita obligatoriamente una de las dos respuestas
posibles: sin (S1 0 no).
printj(H  nDesea continuar sin (si 0 no) ");
while ((car = getche( )) /= 's' && car /= 'n')
printj(H  nDesea continuar sin (si 0 no) ");
EI siguiente programa da como resultado la suma de una serie de can-
tidades introducidas por teclado. La entrada de datos finaliza cuando pu]·
semos hZ.
# include <stdio.h >
# include <stdlib.h>
main( )
[
double sum 0, v;
system("cls"); 1* borrar pantalla */
printj ("Pulse AZ (F6) para jinalizar la entrada  n  n");
printj("  nCantidad > > ");
while (scanj("%lf': &v) /= EOF)
[
printj ("%35.2j n': v);
sum + = v;
printj("  nCantidad > > ");
l
printj ("  t  tTOTAL%14.2j n': sum);
while (1)
sentencia;
main( )
[
while (1)
[
char car;
printj("  nlntroduce un cardcter: ");
car = getche( );
printj(" nEI c6digo ASCII de %c es %d n': car, car);
l
Realizar un programa que imprima 10snumeros z, comprendidos en-
tre 1 y 50, que cumplan la expresi6n:
/ * Cuadrados que se pueden expresar
* como sum a de otros dos cuadrados
*/
# include <stdio.h>
# include <std/ib.h >
# include <math.h >
main( )
(
unsigned int x, y, z;
system (Hcls");
printj("%lOs %lOs %lOs  n': HZ': "X': "Y");
printf("  n  n");
x=l;y=l;
while (x < = 50)
(
/ * cafcufar fa parte entera (z) de fa raiz cuadrada */
z = sqrt(x * x + Y * y);
while (y < = 50 && z < = 50)
{
/ * comprobar si z es suma de dos cuadrados perfectos */
if (z * z = = x * x + Y * y)
printj("%lOd %lOd %lOd n': z, x: y);
y=y+l;
z = sqrt(x * x + y * y);
l
x=x+l;y=x;
Ejecuta una sentencia, simple 0 compuesta, una 0 mas veces, dependiendo
del valor de una expresi6n.
do
sentencia;
while (expresion);
3. Si el resultado de la expresion es cero (falso), se pasa a ejecutar
la siguiente sentencia en el programa.
4. Si el resultado de la expresion es distinto de cero (verdadero), el
proceso se repite comenzando en el punta 1.
Calcular la raiz cuadrada de un numero n, por el metoda de Newton
que dice:
# include <stdio.h >
# include <std/ib.h>
main( )
(
double n;
double aprox;
double antaprox;
double epsilon;
1* mimero *1
1* aproximacion a fa raiz cuadrada *1
I* anterior aproximacion a fa raiz cuadrada */
1* coeficiente de error *1
system (Hcls");
printj(HNlimero: ");
scanf(H%lf': &n);
printj(HRaiz cuadrada aproximada:");
scanf(H%lf': &aprox);
printjt'Coejiciente de error: ");
scanf(H%lj': &epsilon);
do
{
antaprox = aprox;
aprox = (nlantaprox + antaprox) I 2;
}
while (fabs(aprox - antaprox) > = epsilon);
printj(H n  nLa raiz cuadrada de %.2lj es %.2lj n': n, aprox);
}
Numero: 10
Raiz cuadrada aproximada: 1
Coeficiente de error: le-6
Cuando se desea ejecutar una sentencia simple 0 compuesta, repetidamen-
te un numero de veces conocido, la construcci6n adecuada es la sentenciafor.
for ([vI = eI, [v2 = e2]...];[condicion];[progresion-condj)
sentencia;
vi=ei vi representa una variable que sera inicializada con el
valor de la expresi6n ei.
condici6n es una expresi6n de Boole (operandos unidos por ope-
radores relacionales y/o 16gicos). Si se omite, se supo-
ne siempre que es verdadera.
progresi6n-cond es una expresi6n cuyo valor evoluciona en el senti do de
que se de la condici6n para finalizar la ejecuci6n de la
senten cia for.
2.1 Si el resultado es distinto de cero (verdadero), se ejecuta la
sentencia, se evalua la expresi6n que da lugar a la progresion
de la condicion y se vuelve al punta 2.
2.2 Si el resultado de 2 es cero (falso), la ejecuci6n de la senten-
cia for se da por finalizada y se continua en la siguiente sen-
tencia del programa.
for (i = 1; i < = 100; i + +)
printf("%d ': i);
Este ejemplo imprime los numeros dell aliOO. Literalmente dice: desde
i igual a 1, mientras i sea menor 0 igual que 100, con incrementos de 1,
escribir el valor de i.
for (k = 7; k < = 112; k + = 7)
printj(H%d ': k);
float i;
for (i = 1; i < = 10; i + = 0.5)
printjt'%g ': i);
for (a = 9; a > = 1; a--)
printj(H%d ': a);
for (,. ,)
{
Este ejemplo indica c6mo realizar un bucle infinito. La terminaci6n
se realizani con Break 0 con Ctrl +C.
En generallas sentencias repetitivas while, do, y for se pueden colocar in-
distintamente unas dentro de otras para formar bucles anidados.
Un bucle puede colocarse dentro de otro bucle y entonces se dice que
estan anidados. En este caso el bucle interno se ejecutara total mente, cada
vez que se ejecute el bucle que 10 contiene.
Escribir un programa que imprima un triangulo construido con ca-
racteres consecutivos del c6digo ASCII, como el que se muestra a conti-
nuaci6n.
#
0J0 &
( )
# include <stdio.h>
# include <stdlib.h>
main( )
!
char car;
unsigned int ji/as, columnas;
unsigned int njilas;
printj("Ntimero de ji/as del tridngulo ");
scanj("%d': &nji/as);
for (fi/as = J, car = ' x20'; ji/as < = nji/as; ji/as+ +)
I
for (columnas J; columnas < = ji/as; columnas+ +)
(
car+ +;
printf("%5c': car);
J
printf("  n ");
J
1
Imprimir un tablero de ajedrez y sobre eI marcar con * las celdas a
las que se puede mover un alfil desde una posicion dada.
2. Partiendo de la fila 1, columna 1y recorriendo por filas el tablero
imprimir un(a):
* si se cumple, que la suma 0 diferencia de la fila y columna ac-
tuales, coincide con la suma 0 diferencia de la fila y columna
donde se coloca el alfil.
main( )
{
lot falfi/, calfi/,·
iot fila, columna;
/ *posicion del alfil */
/ *posicion actual */
printf(t<Posicion del alfil (fila, columna): ");
scanf(t<%d O/Od':&falfil, &calfil);
for (fila = 1;fila < = 8; fi/a+ +)
{
for (columna = 1; columna < = 8; columna + +)
{
if ((fila + columna = = falfil + calfil) II
(fila - columna = = falfil - calfil))
printj(H * ");
else
if ({fila + columna) % 2 0)
printj(HB ");
else
printj(HN ");
l
printf(H  n"); / * cambiar de fila */
l
l
Esta sentencia, estando dentro de una sentencia do, while, 0 for, pasa el
control para que se ejecute la siguiente iteraci6n.
El siguiente programa imprime todos los numeros entre 1 y 100 que
no sean multiplos de 5.
main( )
!
iot n;
for (n = 0; n < = 100; n + +)
!
if (n % 5 = = 0)
continue;
printf(H%d ': n);
l
l
Notar que cada vez que se ejecuta la sentencia continue, el cuerpo del
for se abandona, iniciandose la ejecuci6n del mismo para un nuevo valor
de n.
La sentencia goto transfiere el control a una linea especifica del programa,
identificada por una etiqueta.
Si la linea a la que se transfiere el control es una sentencia ejecutable,
se ejecuta esa sentencia y las que Ie siguen. Si no es ejecutable, la ejecuci6n
se inicia en la primera sentencia ejecutable que se encuentre a continua-
ci6n de dicha linea.
No se puede transferir el control fuera de la funci6n en la que nos en-
contramos.
# include <stdio.h>
# include <stdlib.h>
main( )
(
float r, a;
.system(Hcls");
printjt'Escriba un cero para jinalizar  n");
Comienzo:
printf(H  nRadio: "); scanf(H%f': &r);
if (r > 0)
(
a = 3.141592 * r * r;
printf(HArea = %.2j n': a);
}
else
goto fin;
goto Comienzo;
fin: ;
J
Un uso abusivo de esta sentencia da lugar a programas dificiles de in-
terpretar y de mantener. Por ello, en programaci6n estructurada, se utiliza
solamente en ocasiones excepcionales. La funci6n que desempefia una sen-
tencia goto, puede suplirse utilizando las sentencias de control estructura-
das (if...else, do, for, switch, while).
El uso mas normal consiste en abandonar la ejecuci6n de alguna es-
tructura profundamente anidada, cosa que no puede hacerse mediante la
sentencia break, ya que esta se limita unicamente a un solo nivel de anida-
miento.
Cuando e1 valor de alguno de los elementos de la matriz m sea igual
a-I, salir.
main( )
[
int f, c, m[8][8];
for if = 0; f < = 7; f + +)
for (c = 0; c < = 7; c+ +)
if (mUl[c] = = -1)
goto salir;
salir:
if if < 8 && c < 8)
printft'(%d,%d)  n': J, c);
1. Si a = 0 y b = 0, imprimiremos un mensaje diciendo que la ecua-
cion es degenerada.
Indicar con literales apropiados, los datos a introducir, as! como los
resultados obtenidos.
# include <stdio.h>
# include <stdlib.h>
# include <math.h >
main( )
{
double a, b, c;
double d;
/ * coeficientes de fa ecuacion */
/ * discriminante */
system("c/s"); / * borrar la pantalia */
printf("Coejicientes a, bye de la ecuaci6n: ");
scanjt'%lj %lj %lj': &a, &b, &c);
printf("  n  n ");
if (a = = 0 && b = = 0)
printj("La ecuaci6n es degenerada  n ");
else
if (a = = 0)
printj("La unica rafz es: %.2lj n': -c / b);
else
[
re = -b / (2 * a);
d=b*b-4*a*c;
im = sqrt(fabs(d)) / (2 * a);
if (d > = 0)
[
printf("Rafces reales: n");
printj("%.2lj %.2lj n': re + im, re - im);
)
else
[
printf("Rafces complejas:  n ");
printj("%.2lj + %.2lj i n': re, jabs(im));
printj("%.2lj - %.2lj i n': re, jabs(im));
)
)
Escribir un programa para que lea un texto y de como resultado el
numero de palabras con al menos cuatro vocales diferentes. Suponemos
que una palabra esta separada de otra por uno 0 mas espacios (' '), carac-
teres tab ( t) 0 caracteres nueva linea (  n).
/ ********** Palabras con cuatro 0 mas vocales diferentes **********/
# include <stdio.h >
# include <stdlib.h >
main( )
(
int np = 0; / * numero de palabras con 4 vocales distintas */
int a = 0, e = 0, i = 0, 0 = 0, u = 0;
char car;
printjt7ntroducir texto. Finalizar la entrada con AZ  n");
while ((car = getchar( )) /= EOF)
(
switch (car)
{
case ~': case 'a':
a = 1;
break;
case 'E': case 'e':
e = 1;
break;
case 'f': case 'i':
i = 1;
break;
case '0': case '0':
o = 1;
break;
case 'V': case 'u':
u = 1;
break;
default:
if (car == " II car == 't' II car == 'n')
{
if ((a + e + i + 0 + u) > = 4)
np + = 1; .
a=e=i=o=u=O;
I
I/*fin del switch */
} / *fin del while */
if ((a + e + i + 0 + u) > = 4)
np + = 1;
printj("  nNumero de palabras con 4 vocales distintas: %d': np);
}
Escribir un programa para que lea un texto y de como resultado el
numero de caracteres, el numero de palabras y el numero de lineas del mis-
mo. Suponemos que una palabra esta separada de -otra por uno 0 mas es-
pacios (' '), caracteres tab ( t) 0 caracteres nueva linea ( n).
# include <stdio.h>
# include <stdlib.h>
const int Sf = 1;
const int NO = 0;
main( ) / *funcian principal */
[
char car;
int palabra = NO;
int ncaracteres = 0, npalabras = 0, nlineas = 0;
printj("fntroducir texto. Finalizar cada /(nea con CR.  n ");
printjt'Finalizar la entrada con AZ.  n  n");
while ((car = getchar( )) 1= EOP)
[
+ +ncaracteres;
if (car = = " II car
palabra = NO;
else
if (palabra = = NO)
[
/ * contador de caracteres */
= = ' n' II car = = ' t')
/ * eliminar blancos, tabuladores y */
/ *finales de linea entre palabras */
/ * comienza una palabra */
+ + npalabras;
palabra = Sf;
if (car = = ' n')
+ +nlineas;
/ *finaliza una linea */
/ * contador de lineas */
)
printj("%d %d %d  n': ncaracteres, npalabras, nlineas);
)
Realizar un programa que a traves de un menu permita, realizar las
operaciones de sumar, restar, multiplicar, dividir y salir. Las operaciones
constanin solamente de dos operandos.
# include <stdio.h >
# include <stdio.h >
main( )
(
double dato1, dato2, resultado;
int operacion;
double sumar(double dato1, double dato2);
double restar(double dato1, double dato2);
double multiplicar(double dato1, double dato2);
double dividir(double dato1, double dato2);
void menu(void);
while (1)
{
do
(
system(Hcls");
menu( );
scanj(H%d': &operacion);
J
while (operacion < 1 II operacion > 5);
if (operacion 1= 5)
(
printf(H  nTeclear dato 1: ");
scanft'%lf: &dato1);
printf(H  nTeclear dato 2: ");
scanj(H%lf: &dato2);
switch (operacion)
(
case 1:
resultado = sumar(dato1, dato2);
break;
case 2:
resultado restar(dato1, dato2);
break;
case 3:
resultado multiplicar(dato1, dato2);
break;
case 4:
resultado = dividir(dato1, dato2);
break;
J
printj(H n  nResultado = %g  n': resultado);
printj(H  nPufse una tecla para continuar ");
getch( );
J
else
break;
void menu( )
(
printj(H  n  tl. sumar  n ");
printj(H  n  t2. restar  n ");
printj(H  n  t3. multiplicar  n");
printj(H  n  t4. dividir  n ");
printf(H  n  t5. salir  n");
printj(H  n  nSefeccione fa operaci6n deseada: ");
J
double sumar(double a, double b)
(
double c;
c=a+b;
return(c);
)
double restar(double a, double b)
[
double c;
c = a - b;
return (c);
J
double multip/icar(double a, double b)
[
double c;
c = a * b;
return(c);
J
double dividir(double a, double b)
[
double c;
c=a/b;
return(c);
J
Un algoritmo que genere una secuencia aleatoria 0 aparentemente aleato-
ria de numeros, se llama un generador de numeros aleatorios. Muchos ejem-
plos requieren de este metodo.
EI metoda mas comunmente utilizado para generar numeros aleato-
rios es el metoda de congruencia lineal. elida numero en la secuencia rk,
es calculado a partir de su predecesor rk-l' utilizando lasiguiente formula:
La secuencia as! generada, es Hamada mas cprrectamente secuencia
pseudoaleatoria, ya que cada numero generado, depende del anterior men-
te generado.
to-
m-
ncia
en-
El siguiente algoritmo, presentado como una fund on C, genera 65536
numeros aleatorios y no causara sobrepasamiento en un ordenador que ad-
mita un rango de enteros de _231 a 231 - 1.
void rnd(long *prandom)
[
La Hamada a esta fundon, pasa el parametro por referenda, con la
finalidad de generar un numero random diferente cada vez. La fundon ge-
nera numeros enteros comprendidos entre 0 y 65535.
Para la mayoria de las aplicaciones, estos numeros deberian estar com-
prendidos dentro de un intervalo requerido. Por ejemplo, si el problema
simula la tirada de un dado, podriamos escribir:
# include <stdio.h >
# include <stdlib.h>
# include <ctype.h >
main( )
[
unsigned int inicio,o
long random = inicio,o / * random
int tirada,o
char c,o
system(Hcls"),o
printf(HPara tirar el dado, pulse una tecla  n "),o
printf(HPara 1inalizar pulse <1>.  n  n"),o
c = getch( ),o
/ * tolower convierte a mimisculas el contenido de c */
while (tolower(c) /= '!')
[
rnd(&random),o
tirada = random % 6 + 1;
printj("%10d%c': tirada, ( r');
c = getch( );
void rnd(long *prandom)
(
Frecuentemente requerimos de un valor aleatorio entre 0 y 1. Para este
proposito podemos utilizar una version modificada como la que se expone
a continuacion:
main( )
{
unsigned int inicio;
long random = inicio; / * random
double n;
int i;
for (i = 10; i; i--)
(
n = rnd(&random);
printj("%.8g  n': n);
1
}
double rnd(long *prandom)
{
*prandom = (25173 * *prandom + 13849) % 65536;
return((double) *prandom / (double)65535);
}
Supongamos que tenemos un solido irregular S, el cual puede encerrarse
en un cubo C. Puede demostrarse que la probabilidad de que un punto al
azar dentro de C, este tambien dentro de S es:
Un octavo de la esfera, as! definida, esta dentro del cubo de lado 1.
Por 10que si generamos un punto a1 azar, 1a probabi1idad de que este se
encuentre tambien dentro del sector esferico es:
Por 10tanto, para saber e1vo1umen de 1aesfera, basta calcu1ar esa pro-
babilidad.
# include < stdio.h >
# include <stdlib.h>
coost iot TOTAL = 1000;
main( )
[
float vofumen; / * vofumen de fa esjera */
iot dentro,o / * ntimero de puntos dentro de fa esjera */
iot DentroEsjera(coost iot);
systemt 'cis");
printj("Ensayos a realizar: %d  n  n': TOTAL);
dentro = DentroEsjera(TOTAL);
/ * Es necesario poner 8.0 para que el resultado sea real */
volumen = 8.0 * dentro / TOTAL;
printj(" n  n  nVolumen estimado = %g  n': volumen);
l
/ * Puntos generados dentro de la esjera */
int DentroEsjera(const int total)
[
unsigned int inicio;
long random = inicio;
int i, dentro = 0;
double x, y, z;
for (i = 1; i < = total; i+ +)
[
printf("Realizando cdlculos... %d%c': i, ' r');
x = rnd(&random); y = rnd(&random); z = rnd(&random);
if (x*x + y*y + z*z < = 1)
dentro = dentro + 1;
l
return( dentro);
l
/* Generador de numeros pseudoaleatorios */
double rnd(long *prandom)
[
*prandom = (25173 * *prandom + 13849) % 65536;
return((double) *prandom / (double)65535);
l
TIPOS ESTRUCTURADOS DE DATOS
Un array es una estructura homogenea, compuesta por varias componen-
tes, todas del mismo tipo y almacenadas consecutivamente en memoria.
Cada componente puede ser accedido directamente por el nombre de la
variablearray seguido de uno 0 mas subindices encerrados entre corchetes.
La representacion de los arrays se hace mediante variables suscritas
o·de subindices y pueden tener una 0 varias dimensiones (subindices). A
los arrays de una dimension se les llama tambien listas; y a los de dos di-
mensiones, tablas.
Desde el punta de vista matematico, en mas de una ocasion necesita-
remos representar variables, tales como:
all al2 al3
a21 a22 a23
si se utilizan dos subindices. Para realizar esta misma representaci6n bajo
C, tendremos que recurrir a los arrays que acabamos de definir y que a
continuaci6n se estudian.
Por ejemplo, supongamos que tenemos un array unidimensionailla-
made datos, el cual contiene tres elementos. Estos elementos se identifica-
rein de la siguiente forma:
N6tese que los subindices son enteros consecutivos, y que el primer
subindice vale O. Un subindice puede ser cualquier expresi6n entera.
Un array de dos dimensiones se representa mediante una variable con
dos subindices (filas, columnas); un array de tres dimensiones se represen-
ta mediante una variable con tres subindices etc. El numero maximo de di-
mensiones 0 el numero maximo de elementos para un array depende de
la memoria disponible.
La dec1araci6n de un array especifica el nombre del array, e1numero de
elementos del mismo y el tipo de estos.
tipo nombre [tamafio};
tipo nombre [ ];
tipo indica el tipo de 10s elementos del array. Puede ser cualquier
tipo excepto void.
tamaiio es una constante que especifica el numero de elementos del array.
EI tamafio puede omitirse cuando se inicializa el array, cuando
se declara como un panimetro formal en una funcion 0 cuando
se hace referencia a un array declarado en otra parte del
programa.
Este ejemplo declara una variable array denominada lista con 100ele-
mentos (del 0 al 99), cada uno de ellos de tipo into
Este ejemplo declara una variable array denominada nombre con 40
elementos (0 a 39), cada uno de ellos de tipo char.
Este ejemplo declara el tipo y el nombre de un array de punteros a
objetos de tipo char. La definicion actual de vector se hace en otra parte
del programa.
Un elemento de un array se puede utilizar exactamente igual que una
variable. Por ejemplo, las siguientes operaciones son validas:
int lista[100], k, a;
a = lista[1] + lista[99J'
k = 50;
lista[k] + = 1;
Notar que para referenciar un elemento de un array se puede emplear
comosubindice una constante, una variable 0 una expresion de tipo entero.
tipo nombre [expr-cte][expr-cte] ...;
tipo nombre [ ][expr-cte] ...;
La primera expr-cte puede omitirse cuando se inicializa el array, cuando
se declara como un panimetro formal en una funcion 0 cuando se hace
referencia a un array declarado en otra parte del programa.
int a[2j[3j[4j[5j[3j;
char b[12j[5j;
int c[ j[3j = [
10, 12, 14,
16, 18, 20
};
Este ejemplo declara un array a de cinco dimensiones. EI numero de
elementos es 2x3x4x5x3, todos de tipo into EI primer elemento es
a[Oj[Oj[Oj[Oj[Ojy el ultimo a[1j[2j[3j[4j[2]. Tambh~n declara un array b de
dos dimensiones, con 12x5 elementos de tipo char (b[Oj[Oja b[1lj[4j), e ini·
cializa el array c de dos filas y tres columnas.
EI lenguaje C no chequea los limites de un array. Es responsabilidad del
programador el realizar este tipo de operaciones.
Para dimensionar un array se pueden emplear constantes 0 expresio·
nes a base de constantes de cualquier tipo entero.
Para acceder a un elemento de un array, se hace mediante el nombre
del array seguido de uno 0 mas subindices, dependiendo de las dimensio·
nes del mismo, cada uno de ellos encerrado entre corchetes. Un subindice
puede ser una constante, una variable 0 una expresion cualquiera.
Algunos compiladores de C, no permiten inicializar arrays de una 0
mas dimensiones si estan declarados como locales (auto). En este caso, para
inicializar un array, debemos definirlo como global; es decir, debe ser una
variable que exista durante toda la ejecucion del programa. Esto se consi-
gue definiendo el array a nivel externo 0 declarandolo como static.
#include < stdio.h >
#dejine N 10
main( )
(
static float x[ J = {1O,15,20,25,30,35,40,45,50,55};
float y[N][NJ, z[2 *NJ; / * declaraci6n de los arrays y, Z */
int f, c;
j=l;c=9;
yUJ[cJ = 20; z[c+ 10J = 30; / * rejerenciando elementos */
printft'%g %g %g  n': x[cJ, YU][cJ, z[c+1O]);
}
Este ejemplo declara e inicializa un array x de una dimensi6n de tipo
real. Tambien declara un array y de dos dimensiones y un array Z de una
dimensi6n ambos de tipo real. A continuaci6n asigna el valor 20 al ele-
mento y[1][9J y el valor 30 al elemento z[19J.
La declaraci6n de un array como local (auto) puede dar lugar a que
el tamafio reservado para la pila sea sobrepasado. Esto se debe a que la
asignaci6n de memoria para las variables locales se hace a traves de la pila.
Cuando esto ocurra, seremos informados mediante un mensaje de error:
stack overflow. La soluci6n a este problema es aumentar el tamafio de la
pila (stack) por medio de la opci6n correspondiente del compilador.
Esta orden compila y enlaza el programa prog.c, utilizando una pila
de tamafio 8K (2000H bytes).
Los arrays son almacenados por filas. Por ejemplo, si inicializamos
un array de 2 filas y 3 columnas de la forma:
( 168 ENCICLOPEDIA DEL LENGUAJE C
Realizar un programa que asigne datos a una matriz unidimensional
a de n elementos y, a continuaci6n, escribir el contenido de dicha matriz.
main( )
{
int a[N--.ELEMENTOS], n = 0, i; / * n = nOde elementos Iddos */
printj(Hlntroducir valores para la matriz.  n");
printjteLa entrada jinalizard cuando se hayan introducido  n");
printf(Hel total de los elementos 0 cuando se introduzca  n");
printf(Hun valor no numerico.  n  n");
printf(Ha[%d]= ': n);
while (n < N--.ELEMENTOS && scanf(H%d': &a[nJ))
{
n+ +;
printj(Ha[%d]= ': n);
}
/ * Salida de datos */
printf(H  n  n ");
for (i = 0; i < n,oi+ +)
printf("%d ': a[iJ),o
printf("  n  nFin del proceso.  n "),o
J
Realizar un programa que lea las notas correspondientesa los alum-
nos de un determinado curso, las almacene en un array y de como resulta-
do la nota media correspondiente al curso.
# include <stdio.h>
# include <stdlib.h>
main( )
[
float notas[ALUM~AX], suma
iut i, nalumnos,o
system(' ecls"),o
printf("Nzimero de alumnos: "),o
scanf("%d': &nalumnos),o
/ * Entrada de datos */
for (i = 1; i < = nalumnos,o i+ +)
[
printf("Alumno nzimero %3d, nota final: ': i);
scanf("%f': &notas[i-1]),o
suma + = notas[i-1],o
/ * Escribir resultados */
printj("  n  nNota media del curso: %5.2f n': suma / nalumnos),o
J
Realizar un programa que asigne datos a una matriz t de dos dimen-
siones y, a continuaci6n, escriba las sumas correspondientes alas filas de
la matriz.
#include <stdio.h >
# include <stdlib.h >
# define FILAS---.MAX 10
#define COLS---.MAX 10
/ * mimero maximo de filas */
/ * mimero maximo de columnas */
main( )
{
float tfFILAS---.MAXj[COLS---.MAXj, sumafila,o
int filas, cols, f, c,o
printj("Numero de filas de la matriz: "),o
scanf("%d': &filas);
printj("Numero de columnas de la matriz: "),o
scanj("%d': &cols),o
/ * Entrada de datos */
for if = 0;f < filas,of+ +)
{
printjt'  nDatos para la fila %d  n': f);
for (c = 0; c < cols,oc++)
scanj("%f': &tUj[c}),o
/ * Escribir la suma de cada fila */
printj("  n  n"),o
for if = 0;f < filas; f+ +)
{
sumafila = 0;
for (c = 0; c < cols; c++)
sumafila + = t[f][c];
printf(HFila %d, suma = %g  n': j, sumafila);
]
1
Rea1izar un programa que a1macene en una matriz 10s tantos por ciento
de aprobados correspondientes a 10s cursos de BUP en 10s alios 1986 a 1990
y, a continuaci6n, permita consu1tar dicha matriz.
/ * Tanto por ciento de aprobados en los cursos 1~ 2 a y 3 a
* de BUP en los afios 1986 a 1990.
*/
#include <stdio.h>
#include <stdlib.h>
#define AC 1986
#define AF 1990
#define C (AF - AC + 1)
main( )
{
iut curso, anno;
char x;
/ * Asignar valores a la matriz */
static float est! ][C] = {
70,68,73,69,71,
80,83,81,85,84,
79,83,81,85,82
];
/ * Presentar un dato cua1quiera */
do
{
system(Hcls");
do
[
printj("Curso que desea consultar (1, 2 0 3) ");
scanj("%d': &curso);
1
while (curso < 1 II curso > 3);
printjt'  n ");
do
[
printj("Ano (%d a %d) ': AC, AF);
scanj("%d': &anno);
1
while (anno < AC II anno > AF);
printj("  n");
printj("Curso %dO de BUP. Ano %d; ': curso, anno);
printj("lo superaron el %.1j %%': est[curso-1][anno-AC});
printjt' n  n  n");
printj("Mds consultas sin: ");
do
x = getche( );
while (x /= 's' && x /= 'n');
1
while (x / = 'n');
1
Realizar un programa para que lea una lista de valores. A continua-
cion, y sobre la lista, encontrar los valores maximo y minima, y escribirlos.
# include <stdio.h>
# include <stdlib.h>
main( )
(
float a[DIM~AXJ, max, min;
int numval = 0, i; / * numval = nOde valores leidos */
/ * Entrada de datos */
system("cls' ');
printjt'Introducir valores y jinalizar pulsando AZ.  n");
printf("a[%dJ = ': numval);
while (numval < DIM~AX && scanf("%f': &a[numval]) 1= EOF)
(
numval+ +;
printf("a[%dJ = ': numval);
I
/ * Encontrar los valores maximo y minimo */
if (numval > 0)
(
max = min = a[OJ;
for (i = 0; i < numval; i+ +)
(
if (a[i] > max)
max = a[i];
if (a[iJ < min)
min = a[i];
I
/ * Escribir resultados */
printf("  n  nValor maximo: %g, valor minimo: %g  n': max, min);
I
else
printf("  n  nNo hay datos.  n");
Una cadena de caracteres es un array unidimensional, en el cual todos sus
elementos son de tipo char.
Un array de caracteres puede ser inicializado asigmindole un literal.
Por ejemplo:
Este ejemplo inicializa el array de caracteres cadena con cinco elementos
(cadena[Oj a cadena[4/). El quinto elemento, es el canicter nulo ( 0), can
el cual C finaliza todas las cadenas de caracteres.
Si se especifica el tamafio del array de caracteres y la cadena asignada
es mas larga que el tamafio especificado, se obtiene un error en el momen-
to de la compilaci6n. Por ejemplo:
Este ejemplo daria lugar a un mensaje de error, indicandonos que he-
mos excedido los limites del array.
Si la cadena asignada es mas corta que el tamafio del array de caracte-
res, el resto de los elementos del array son inicializados a valor nulo (  0).
Este ejemplo declara el array denominado nombre_apellidos como
una cadena de caracteres de longitud maxima 60.
Este ejemplo declara el array denominado UsIa como un array de ca-
denas de caracteres. Esto es, UsIa es un array de 100 filas, cada una de las
cuales es una cadena de caracteres de longitud maxima 60.
Antes de leer una cadena de caracteres, debe declararse el array de tipa
char que la va a contener. La dimensi6n de este array debe corresponderse
con el numero de caracteres maximo que puede contener la cadena, mas
uno correspondiente al caracter nulo de terminaci6n. Por ejemplo, si que-
remos leer un nombre de 40 caracteres de longitud maxima, debemos de-
clarar el array de la forma siguiente:
Para leer esta cadena de caracteres, podemos emplear la funcion
scanf( ). En este caso, la variable nombre, no necesita ser precedida por
el operador &, porque nombre es una direccion, la direccion de comienzo
del array. Si se lee elemento a elemento, el operador & es necesario.
Lafuncion gets( ) lee una linea de la entrada estandar, stdin, y la almacena
en la variable especificada. Esta variable es un puntero a la cadena de ca-
racteres leida.
La variable var, contiene todos los caracteres tecleados, excepto el ca-
racter nueva linea (  n), que es automciticamente reemplazado por el ca-
dcter nulo ( 0), con el cual C finaliza toda cadena de caracteres.
La funcion gets( ) devuelve un puntero al valor leido. Un valor nulo
para este puntero, indica un error 0 una condicion de fin de fichero (eo!).
La funcion gets( ), a diferencia de la funcion scanf( ), permite la en-
trada de una cadena de caracteres formada por varias palabras separadas
par espacios en blanco, sin ningun tipo de formato. Recordar que para
scanf( ), el espacio en blanco actua como separador de datos en la entrada.
La funcion puts( ) escribe una cadena de caracteres en la salida estandar
stdout, y reemplaza el caracter nulo de terminacion de la cadena (  0) por
el caracter nueva linea ( n).
La fundon puts( ) retorna un valor positivo si se ejecuta satisfactoria-
mente; en caso contrario, retorna el valor EOP.
# include <stdio.h>
# include <conio.h >
char linea[81];
char *pc;
main( )
{
printft'Introduce una cadena de caracteres: ");
pc = gets(linea);
printjt'  nLa linea introducida es:  n");
printj("%s  n': linea);
puts("Pulse una tecla para continuar");
getch( );
puts("  nLa escribo por segunda vez:");
puts(pc);
}
Las fundones scanf( ), getchar( ), y gets( ) tienen una caracteristica comtin:
leen los datos requeridos de la entrada estandar referendada par stdin. Es
necesario tener presente que los datos, cuando son tecleados, no son leidos
directamente del dispositivo, sino que estos son depositados en una memo-
ria inter media (buffer), asociada con el dispositivo de entrada. Los datos
son leidos de la memoria intermedia cuando son validados; esto ocurre cada
vez que pulsamos Enter.
Recordar que las funciones scanf( ) y getchar( ) interpretan el caracter
,  n' (nueva linea) como un separador. En modo texto y bajo DOS, pulsar
la tecla Enter equivale a transmitir dos caracteres: CR + LF (estos en UNIX
equivalen a un solo canicter: '  n'; ver capitulo 8). Esto quiere decir que
el canicter que actua como separador es CR; quedando en el buffer aso-
ciado con stdin, el canicter LF. Este es un canicter valido para la funcion
gets(), entre otras; 10 que quiere decir que cuando estas funciones se com-
binan en un mismo programa, hay problemas de lecturas no deseadas. La
solucion a esto es limpiar el buffer asociado con stdin despues de una lec-
tura con las funciones scanj( ) 0 con getchar( ), y antes de una lectura con
la funcion gets( ).
# include <stdio.h>
# include <stdlib.h>
# include <ctype.h >
main( )
I
int entero;
double real;
char respuesta
system("cls '');
/ * Introducir mimeros */
printf("Introducir un nO entero y un nO real:  n");
scanf("%d %!j': &entero, &real);
printj("%d + %f = %f n  n': entero, real, entero + real);
/ * Leer 4 cadenas de caracteres */
printf("Introducir 4 cadenas de caracteres con scan!'  n");
for (entero = 0; entero < 4; entero + +)
I
scanj("%s': cadena);
printf("%s  n': cadena);
1
/ * Limpiar el buffer de entrada y leer una cadena con gets */
fflush( stdin );
printj("Introducir cadenas para gets.  n");
while (respuesta = = is' && gets(cadena) != NULL)
(
printj("%s  n': cadena);
do
(
printjt'l. Desea continuar (sin) ");
respuesta = getchar( );
J
while ((respuesta!= is') && (respuesta!= en'));
1* Limpiar el buffer de entrada *1
fflush( stdin );
J
J
Este programa utiliza la funci6n scanf( ), para leer datos numericos
y de caracteres. Segun 10 expuesto en el parrafo anterior, en el buffer aso-
ciado por el sistema a stdin queda el caracter LF; a continuaci6n, este ca-
racter sera leido par la funci6n gets( ), sustituyendo asi, ala primera cade-
na que tendria que leer esta funci6n~ Este problema se evita limpiando el
buffer de stdin con la funci6nfflush(), antes de realizar la lectura con gets( j.
Con la funci6n getchar( ), nos ocurre el mismo problema que con scanf( j.
Examinar una cadena de caracteres. Leer una cadena de caracteres y,
a continuaci6n, escribir por cada caracter, su direcci6n, el caracter y su
valor ASCII.
# include <stdio.h >
# include <stdlib.h >
main( )
{
char cadena[41];
iot i = 0;
system (<tcls''),o
puts(<tEscriba una cadena de caracteres: "),o
gets(cadena);
/ * Examinar cadena d
do
[
printf(<tDirecci6n = 0/05u cardcter= '%c' c6digo ASCII = 0/04d n':
&cadena[ij, cadena~h cadena~]k
i+ +,o
J
while (cadena[i]!= ' 0');
J
Escribir un programa, que uti lice una funci6n para leer una linea de
la entrada y que de como resultado, la linea leida y su longitud 0 numero
de caracteres.
# include <stdio.h>
# include <stdlib.h>
# define CARS~INEA 81
int leer_linea(char linear J),o
main( )
[
char linea[CARS~INEA],o
int long_linea = 0;
/ * array de caracteres */
/ * longitud de una /{nea leida */
system("cls"),o / * limpiar pantalla */
puts("I ntroducir texto: "),o
/ * Se llama a la funci6n leer_linea, pasando como pardmetro
* el array linea. Un array siempre es pasado por referencia.
*/
long_linea = leer_linea(linea),o
printj(H  nLinea leida:  n");
puts(linea);
printj(HLongitud: %d  n': long_linea);
J
/**************************************************************
Funci6n leer lfnea
**************************************************************/
/ * Lee una linea (linea[CARS-.LINEAJ) y calcula su longitud */
int leer_linea (char linear J)
[
int long_linea = 0;
while ((linea[long_lineaJ = getchar( )) != f  n' &&
long_linea < CARS-.LINEA-l)
+ + long_linea;
/ * Si la entrada excede la longitud maxima, se trunca. En
* cualquier caso la cadena se jinaliza con el caracter nulo
* t0'), convenio utilizado por C.
*/
linea[long_lineaJ = f  0';
return long_linea;
J
Realizar la funci6n anterior' 'leer linea" utilizando ahora las funcio-
nes gets( ) y puts( ).
/**************************************************************
Funci6n leer linea
**************************************************************/
/ * Lee una linea (linea[CARS-.LINEAJ) y calcula su longitud */
int leer_linea (char linear J)
[
int i = 0;
gets(linea);
while (linea[i + +] /= <  0 ');
return (i-I);
J
Escribir un programa que lea una linea de la entrada y la almacene
en un array de caracteres. A continuaci6n, utilizando una funci6n, conver-
tir los caracteres escritos en mayusculas, a minusculas.
# deJine LONG--.MAX 81 ·1* longitud maxima de la cadena *1
void Mayusculas_minusculas(char str[ J);
main() 1* Juncian principal *1
!
char cadena[LONG--.MAX];
int i = 0;
printj ("Introducir cadena: ");
while ((cadena[i] = getchar( )) /= < n' && i < LONG--.MAX - 1)
+ +i;
cadena[i] = <  0';
Mayusculas_minusculas(cadena); 1* llamada a la Juncian *1
printj ("%s  n': cadena);
J
/**************************************************************
Funcian Mayusculas_mimisculas
**************************************************************/
/ * Convierte mayusculas a minusculas *1
void Mayusculas_minusculas(char str[ J)
!
int i;
for (i = 0; str[i]!= ' 0'; + +i)
if (str[i) > = :4' && str[i] < = '2')
str[i] = str[i] + 'a' - :4';
Observar que cuando dimensionamos un array de caracteres, por ejem-
plo a un valor de LONG-.MAX, los indices de los elementos disponibles
van desde 0 a LONG-.MAX-1. En el ejemplo vemos que si la entrada ex-
cede la longitud maxima establecida, la cadena se trunca y se finaliza con
caracter nulo ('  0'), convenio utilizado por C.
Un array de cadenas de caracteres es un array donde cada elemento es a
su vez un array de caracteres. Dicho de otra forma, es un array de dos di-
mensiones de tipo char.
El siguiente ejemplo, lee una lista de nombres y los almacena en un
array.
# include <stdio.h >
# include <stdlib.h>
#define MAX 10
#define LONG 60
/ * maximo mimero de nombres */
/ * mimero de caracteres maximo por nombre */
main( )
[
char lista[MAXJ[LONG];
char 4in; / * valor devuelto por gets */
iot i = 0, n; / * Indices */
puts(HParajinalizar la entrada pulse etrl +Z (EOF).");
do
(
printf(H  nNombre y Apellidos: ");
/ * Un EOF hace que gets devuelva un puntero nulo (NULL) */
fin = gets(lista[i+ +J);
J
while (fin != NULL && i < MAX);
/ *Escribir datos */
printf(H  n  n");
for (n = 0; n < i; n + +)
printft'%s  n': lista[n));
Lasdeclaraciones de las funciones, para manipular cadenas de caracteres,
que a continuacion se describen, estan en el fichero a incluir string.h. La
sentencia:
Esta fundon afiade la cadena2 a la cadenal, termina la cadena resultante
con el caracter nulo y devuelve un puntero a cadena!.
Esta funci6n devuelve un puntero a la primera ocurrencia de c en cadena
o un valor NULL si el canicter no es encontrado. EI canicter c puede ser
un canicter nulo ('  0').
<0 si la cadena! es menor que la cadena2,
=0 si la cadena! es igual a la cadena2 y
>0 si la cadena! es mayor que la cadena2.
Esta funci6n copia la cadena2, incluyendo el canicter de terminaci6n nulo,
en la cadenal y devuelve un puntero a cadenal.
Esta funci6n da como resultado la posici6n (subindice) del primer canicter
de cadenal, que pertenece al conjunto de caracteres contenidos en cadena2.
Este valor corresponde a la longitud de la subcadena de cadenal formada
por caracteres no pertenecientes a cadena2. Si ningun canicter de cadena!
pertenece a cadena2, el resultado es la posici6n del canicter de terminaci6n
(  0) de cadenal; esto es, la longitud de cadenal.
Esta funcion devuelve la longitud en bytes de cadena, no incluyendo el ca-
racter de terminacion nulo.
# include <stdio.h>
# include <string.h>
main( )
[
char cadenal[80];
static char cadena2[80]
int n;
char *r;
strcpy(cadenal, "abc");
r = strcat(cadenal, "dej");
printj("%s  n': r); / * escribe: abcdej */
r = strchr(cadenal, Cd');
printf("%s  n': r); / * escribe: dej */
n = strcmp(cadenal, cadena2);
printf("  "%s " es %s  "%s "  n': cadenal,
n ? (n > 0 ? "mayor que" : "menor que") : "igual a':cadena2);
/ * escribe: "abcdej" es men or que "xyz" */
n = strcspn(cadenal, "edc");
printf("La posicion de e, doc en %s es %d  n': cadenal, n);
/ * escribe: La posicion de e, doc en abcdej es 2 */
printf("EI tamano de cadena2 es %d  n': strlen(cadena2));
/ * escribe: El tamano de cadena2 es 3 */
1
Esta funci6n aiiade los primeros n caracteres de cadena2 a la cadenal, ter·
mina la cadena resultante con el canlcter nulo y devuelve un puntero a
cadenal. Si n es mayor que la longitud de cadena2, se utiliza como valor
de n la longitud de cadena2.
Esta funci6n compara los primeros n caracteres de cadenal y cadena2, dis·
tinguiendo mayusculas y minusculas, y devuelve un valor:
<0 si la cadena! es menor que la cadena2,
=0 si la cadena! es igual a la cadena2 y
>0 si la cadena! es mayor que la cadena2.
Si n es mayor que la longitud de la cadenal, se toma como valor la
longitud de la cadenal.
Esta funci6n copia n caracteres de la cadena2, en la cadenal (sobreescri·
biendo los caracteres de cadenal) y devuelve un puntero a cadenal. Si
es menor que la longitud de cadena2, no se aiiade automaticamente un ca
racter nulo a la cadena resultante. Si n es mayor que la longitud de cadena2
la cadenal es rellenada con caracteres nulos ('  0') hasta la longitud n,
Esta funci6n devuelve un puntero a la ultima ocurrencia de c en cadeni
o un valor NULL si el caracter no es encontrado. El caracter c puede s
un caracter nulo ('  0'). '
Esta funci6n da como resultado la posici6n (subindice) del primer canlcter
de cadenal, que no pertenece al conjunto de caracteres contenidos en
cadena2. Esto es, el resultado es la longitud de la subcadena inicial de
cadenal, formada por caracteres pertenecientes a cadena2.
Esta funci6n devuelve un puntero a la primera ocurrencia de cadena2 en
cadenal 0 un valor NULL si la cadena2 no se encuentra en la cadenal.
Esta funci6n lee la cadenal como una serie de cero 0 mas elementos basi-
cos separados por cualquiera de los caracteres expresados en cadena2, los
cuales son interpretados como delimitadores.
Una vez leido el primer elemento de cadenal, la siguiente Hamada a
strtok( ), para leer el siguiente elemento, se efectua poniendo NULL en lu-
gar del argumento cadenal. Observar el ejemplo que se expone a conti-
nuaci6n.
Esta fund6n devuelve un puntero por cada elemento en cadenal. Cuan-
do no hay mas elementos, se devuelve un puntero nulo.
Puede ponerse mas de un delimitador entre elemento y elemento. Tam-
bien pueden variarse el conjunto de caracteres que actuan como delimita-
dores, de una Hamada a otra.
# include <stdio.h>
# include <string.h >
char *cadena = Cfestacadena, estd jormada por varias palabras";
char ~lemento;
main( )
{
elemento = strtok(cadena, Cf ,"};
while (elemento != NULL)
{
printj(Cf%s  n': elemento};
elemento = strtok(NULL, Cf , "};
esta
cadena
esta
formada
par
varias
palabras
Los argumentos empleados en estas funciones, cadena, cadena! y
cadena2, deben contener como caracter de terminaci6n el caracter nulo
('  0').
Esta funci6n asigna memoria para almacenar cadena, copia cadena en el
espacio de memoria asignado y devuelve un puntero a la cadena copiada.
Esta funcit'>nda como resultado el mensaje de error correspondiente al nu-
merode error oro_error. Este numero de error normalmente viene dado
por la variable predefinida errno.
Conviertelas letras mayusculas de cadena, en minusculas. El resultado es
la propia cadena en minusculas.
Conviertelas letras minusculas de cadena, en mayusculas. El resultado es
la propia cadena en mayusculas.
Estafunci6n sustituye todos los caracteres de cadena, por el canicter c, ex-
cepto el canicter de terminaci6n  O.
Cuando las funciones atof( ), atoi( ) y atol( ) leen un canicter que
es reconocido como parte de un numero, dejan de leer caracteres de la
riable cadena.
# include <stdio.h >
# include <stdlib.h >
main( )
!
char *cadena = "-123.45E+05";
double r;
r = atof(cadena);
printf("%E': r);
l
Convierte un numero real a una cadena de caracteres. La cadena de carac-
teressera finalizada con el caracter nulo. Esta funci6n devuelve un punte-
ro a la cadena de caracteres.
decs numero de digitos despues del punta decimal. Si es necesario,
se afiaden ceros.
pdec devuelve un puntero a un valor entero que especifica la posi-
ci6n del punto decimal.
signo devuelve un puntero a un valor 0, si el numero es positivo; 0
un valor distinto de 0, si el numero es negativo.
Convierteun valor entero a una cadena de caracteres. La cadena de carac-
teressera finalizada con el caracter nulo. Esta funci6n devuelve un punte-
ro a la cadena de caracteres.
es la base (2 - 36) en la que viene especificado el valor. Si la
base es 10 y el valor negativo, el primer canicter almacenado
en cadena es el signa menos.
Convierte un valor entero en formato largo, a una cadena de caracteres.
La cadena de caracteres sera finalizada con el caracter nulo. Esta fundon
devuelve un puntero a la cadena de caracteres.
es la base (2 - 36) en la que viene especificado el valor. Si la
base es 10 y el valor negativo, el primer caracter almacenado
en cadena es el signa menos.
# include <stdio.h>
# include <stdlib.h>
main( )
{
double valor = 3.1415926;
int decimales = 8, punto_d, signo;
char *cadena;
cadena = jcvt(valor, decimales, &punto_d, &signo);
printf("cadena = %s  n': cadena);
printj("signo = %d  n': signo);
printjt'posici6n punta decimal = %d  n': punto_d);
J
cadena = 314159260
signa = 0
pasici6n punta decimal 1
FUNCIONES PARA CLASIFICACION Y CONVERSION DE
CARACTERES
Las declaraciones para las funciones, que a continuaci6n se describen, para
clasificar y convertir caracteres, estan en el fichero a incluir ctype.h ( # include
<ctype.h ».
Comprueba si c es un canicter de control (0 - 31 y 127).
int iscntrl(int c);
Comprueba si c es un digito ('0' - '9').
int isdigit(int c);
Comprueba si c es un canicter escribible, exceptuando el espacio (33 - 126).
int isgraph(int c);
Comprueba si c es una letra minuscula ('a' - 'z').
int islower(int c);
isprint(c)
Comprueba si c es un caracter escribible (32 - 126).
int isprint(int c);
Comprueba si c es un caracter de puntuaci6n.
int ispunct(int c);
Todas estas funciones devuelven un valor distinto de 0 si se satisface
la condici6n, y 0 en caso contrario.
Pone a 0 todos los bits de c, excepto los 7 bits de menor orden. Dicho de
otra forma, convierte c a un canicter ASCII.
Las funciones toascii( ), tolower( ) y toupper( ) devuelven el canicter
convertido, si procede. Las funciones tolower( ) y toupper( ) tambien estan
definidas en stdlib.h.
# include <stdio.h>
# include <ctype.h >
# include <stdlib.h>
main( )
[
int c;
char car;
system("cls");
for (c = 0; c < 128; c+ +)
[
printj("Cardcter%4d %c %s': c, iscntrl(c) ? ' x20' : c,
isascii(c) ? "ASCII" : "");
printf("%3s': iscntrl(c) ? "C" : ""); 1* Control *1
printj("%4s': isalnum(c) ? "AN" : ""); 1* AljaNumcfrico *1
printf("%3s': isalpha(c) ? '~" : ""); 1* Aljabetico *1
printj("%3s': ispunct(c) ? "P" : ""); 1* Puntuaci6n *1
putchar('  n');
if (c % 20 = = 0 && c != 0)
[
printj("  nControl, AljaNumerico, Aljabetico, Puntuaci6n ");
printj("  n  n/,Desea continuar? sin ");
car = getche( );
if (tolower(car)
break;
else
system (Hcls");
}
}
}
Una estructura es un nuevo tipo de Idatos que puede ser manipulado de
la misma forma que los tipos predefinidos como float, int, char, entre otros.
Una estructura se puede definir como una coleccion de datos de diferent~s
tipos, logicamente relacionados. En C una estructura solo puede contener
dec1aracionesde variables. En otros compiladores, este tipo de construc-
danes son conocidas como registros.
Crear una estructura es definir un nuevo tipo de datos, denominado tipo
estructura y declarar una variable de este tipo. En la definicion del tipo
estructura, se especifican los elementos que la componen as! como sus ti-
pas. Cada elemento de la estructura recibe el nombre de miembro (campo
del registro). La sintaxis es la siguiente:
struct tipo_estructura
{
declaraciones de los miembros
};
Despues de definir un tipo estructura, podemos declarar una variable
de ese tipo, de la forma:
Para referirse a un determinado miembro de la estructura, se utiliza
la notaci6n:
struct Jicha / * dejinicion del tipo estructura Jicha */
[
char nombre[40);
char direccion[40);
long telejono;
];
Este ejemplo define las variables varl y var2, de tipo!icha, por 10 que
cad a una de las variables consta de los miembros: nombre, direccion y te-
lejono.
Una variable que es un miembro de una estructura, puede utilizarse
exactamente igual que cualquier otra variable.
varl.telejono = 232323;
gets(var2.nombre);
La declaraci6n de las variables varl y var2, puede realizarse tambien
directamente de la siguiente forma:
struct Jicha
[
char nombre[40);
char direccion[40);
long telejono;
] varl, var2;
o tambien, sin dejar constancia del nuevo tipo definido, forma que no se
aconseja:
struct
(
char nombre[40);
char direccion[40};
long telejono;
] varl, var2;
La declaracion de un miembro de una estructura no puede contener
calificadores de clase de almacenamiento extern, static, auto 0 register y
no puede ser inicializado. Su tipo puede ser: fundamental, array, puntero,
union, estructura 0 funcion.
Para declarar un miembro como una estructura, es necesario haber
declarado previamente ese tipo de estructura. En particular una estructura
st no puede contener un miembro de tipo st, pero sf puede contener un
puntero 0 referencia a un objeto de tipo st.
struct jecha
(
int dia, mes, anyo;
];
struct Jicha
(
char nombre[40);
char direccion[40};
long telejono;
fecha jecha_alta;
];
Este ejemplo define la variable persona, en la que el miembro
jecha_alta es a su vez una estructura.
Los miembros de una estructura son almacenados secuencialmente, en
el mismo orden en el que son declarados.
Con una variable declarada como una estructura, pueden realizarse las si-
guientes operaciones:
• coger su direcci6n por medio del operador &
• acceder a uno de sus miembros
• asignar una estructura a otra con el operador de asignaci6n.
Cuando los elementos de un array son de tipo estructura, el array recibe
el nombre de array de estructuras 0 array de registros. Esta es una cons-
trucci6n muy uti! y potente.
El siguiente programa lee una lista de alumnos y sus correspondientes
notas de final de curso, dando como resultado el tanto por ciento de alum-
nos aprobados y suspendidos.
# include <stdio.h>
# include <stdlib.h>
# deJine NA 10 / * mimero maximo de afumnos */
main( )
{
struct Jicha
{
char nombre[60];
float nota;
};
struct Jicha afumnos[NA]; / * array de estructuras 0 registros */
iot n = 0, i;
char 4in;
float aprobados
/ *puntero af nombre feldo */
0, suspensos = 0;
/ * Entrada de datos */
system (HclsH);
printj(HFinalizar la entrada de datos con AZ  n  nH
);
printj(HNombre H);
fin = gets(alumnos[n}.nombre);
while (n < NA && fin != NULL)
{
printj(HNota H);
scanj(H%f': &alumnos[n + +}.nota);
fflush(stdin); / * eliminar el retorno de carro */
printj(HNombre H);
fin = gets(alumnos[n}.nombre);
)
/ * Escribir resultados */
system(Hcls H);
for (i = 0; i < n; i+ +)
if (alumnos[i}.nota > = 5)
aprobados+ +;
else
suspensos + +;
printft'Aprobados %.2g %%  n': aprobados/n*100);
printj("Suspensos %.2g %%  n': suspensos/n *100);
)
Una union es una variable que puede contener miembros de diferentes ti-
pos, en una misma zona de memoria.
La declaraci6n de una union tiene la misma forma que la declaraci6n
de una estructura, excepto que en lugar de la palabra reservada struct se
pone la palabra reservada union. Todo 10 expuesto para las estructuras, es
aplicable alas uniones, excepto la forma de almacenamiento de sus
miembros.
declaraciones de los miembros
};
union tipo_union
{
Despues de definir un tipo uni6n, podemos declarar una 0 mas varia-
bles de ese tipo de la forma:
Para referirse a un determinado miembro de la uni6n, se utiliza la no-
taci6n:
Para almacenar los miembros de una uni6n, se requiere una zona de
memoria igual a la que ocupa el miembro mas largo de la uni6n. Todos
los miembros son almacenados en el mismo espacio de memoria y comien-
zan en la misma direcci6n. El valor almacenado es sobreescrito cada vez
que se asigna un valor al mismo miembro 0 a un miembro diferente.
union tipo_union
{
char varl;
int var2;
float var3;
};
Este ejemplo declara una variable var_union que representa una union
con tres miembros. Esta variable debe ser 10 suficientemente grande como
para contener el mayor de los tres miembros. Cualquiera de los tres miem-
bros pueden asignarse a var_union y utilizarse en expresiones. Es respon-
sabilidad del programador recordar cual es el miembro que hay en la union.
El esquema siguiente, expresa c6mo se ve graficamente var_union.
var_union
I I I I
E
varl -l
~
.1
var2
var3
Si en lugar de haber declarado una union hubiesemos declarado una
estructura, los miembros sedan individuales, esto es:
struct /ibro
I
unsigned edicion;
unsigned anno;
J;
union /ibro-revista
I
struct /ibro /ibros;
char nomrev[30};
J;
structJicha
I
unsigned numreJ;
char titu!o[30};
char autor[20];
char editor[25];
int clase-publicacion;
union libro---fevista lr;
];
Este ejemplo, define una estructura variable, apoyandose en una va-
riable lr de tipo union. Esta variable contendra, 0 bien los datos edicion
y anno, 0 bien nomrev.
Consideremos una biblioteca compuesta por libros y revistas. Por cada
libro 0 revista, figura la siguiente informacion:
Numero de referencia.
TItulo.
Nombre del autor.
Editorial.
Clase de publicacion (libro 0 revista).
Numero de edicion (solo libros).
Ano de publicacion (solo libros).
Nombre de la revista.
1. Almacenar en un array la informacion correspondiente a la bi-
blioteca.
# include <stdio.h >
# include <stdlib.h >
# include <ctype.h >
# define N 10
enum clases
(
libra, revista
J;
struct Jicha 1* registro variable *1
(
unsigned numreJ;
char titulo[30];
char autor[20];
char editor[25];
enum clases libro-'evista;
union
(
struct
(
unsigned edicion;
unsigned anno;
J libros;
char nomrev[30];
J Ir;
J;
main( ) 1* Junci6n principal *1
(
void escribir(int);
void leer(int);
int k = 0;
char resp = 's';
while (tolower(resp) = = 's' && k < N)
(
leer(k + +); I * leer un registro *1
printj("  n  n;. Mas datos a introducir ? sin ");
resp = getche( );
J
escribir(k); 1* listar todos los registros *1
J
1**************************************************************
Funci6n para leer un registro (Jicha)
**************************************************************/
void leer(int k)
{
system (Hcls ");
printjt'INTRODUCIR DATOS  n  n");
printf(HNtimero de refer. ");
scanft' % u' :&bibli[k].numref); fflush(stdin);
printj(HTftulo "); gets(bibli[k].titulo);
printft'Autor "); gets(bibli[k].autor);
printf(HEditor "); gets(bibli[k].editor);
printj(HLibro 0 revista (0 libro, 1 = revista) ");
scanf(H%d': &clase);fflush(stdin);
if (clase = = libro)
{
bibli[k}.libro_revista = libro;
printf(HEdici6n ");
scanf(H % u': &bibli[k}.lr.libros.edicion);
printf(''Ano de public. ");
scanft '%u': &bibli[k}.lr.libros.anno);
l
else
{
bibli[k].libro-.Jevista = revista;
printj(HNombre revista "); gets(bibli[k}.lr.nomrev);
l
l
1**************************************************************
Funci6n para listar todos los registros
**************************************************************/
void escribir(int totaLJichas)
{
int k;
system(Hcls");
printf(HLISTADO DE LIBROS Y REVISTAS  n");
for (k = 0; k < totaL../ichas,o k + +)
[
printj(H  n "),o
printf(H%d %s  n': bibli[k].numref, bibli[k].titulo),o
printj(H%s - Ed. %s  n': bibli[k].autor, bibli[k].editor),o
switch (bibli[k].libro_revista)
[
case libro :
printjt'Edici6n %u - ano %u  n':
bibli[k].lr.li bros.edic ion,
bibli[k].lr.libros.anno ),o
break;
case revista :
printj(H%s  n': bibli[k].lr.nomrev),o
J
J
J
Un campo de bits es un conjunto de bits adyacentes dentro de una unidad
direccionable. Una declaracion de un campo de bits tiene la forma:
La expresi6n constante especifica e1numero de bits en el campo y debe
ser un valor entero no negativo. EI tipo tiene que ser entero (signed 0 un-
signed). No se permiten arrays de campos de bits, punteros a campos de
bits 0 funciones que retornen un campo de bits. EI nombre de un campo
es opcional. Los campos sin nombre sirven de relleno.
Es posible definir como miembros de una estructura (no de una union)
campos de bits. EI tamafio de un campo de bits no debe sobrepasar el ta-
mafio flsico de la palabra maquina, es decir, e1 espacio ocupado por un
entero. Si esto ocurre, el campo siguiente se alinea con respecto al siguiente
entero. Un campo de bits sin nombre y de tamafio 0, garantiza que el si-
guiente miembro de la lista comience en e1siguiente espacio para un entero.
struct palabra / *palabra de 16 bits - 0 a 15 */
{
unsigned car_ascii : 7; / * bits 0 a 6 */
unsigned bit-paridad: 1; / * bit 7 */
unsigned operacion : 5; / * bits 8 a 12 */
unsigned : 2; / * bits 13 a 14 de rel/eno d
unsigned bit-signo : 1; / * bit 15 */
};
main( ) / *junci6n principal */
{
printj("campo
printjt'bit de signa
printf("operaci6n
printj("bit de paridad
printjt'cardcter ASCII
}
%x  n  n': campo);
%x  n': campo.bit-signo);
%x  n': campo.operacion);
%x  n': campo.bit-paridad);
%x  n': campo.car_ascii);
bit de signa : 0
aperaci6n : Ie
bit de pari dad : 1
canlcter ASCII : 43
15 14 13 12 11 10 9 8 7
10 0 0 111 1 1 011
5 4 3 2
o 010 0
La alineaci6n de los campos de bits depende de la implementaci6n.
Los campos son asignados de izquierda a derecha 0 de derecha a izquier-
da, caso del ejemplo mostrado, segun la maquina que se emplee.
Para comprender las ventajas de la utilizaci6n de los campos de bits,
pensemos en construir una estructura para contener la fecha. Tal estructu-
ra puede ser la siguiente:
struct jecha
[
unsigned dia;
unsigned mes;
unsigned anyo;
);
Esta estructura requiere un total de seis bytes. En cambio, si utiliza-
mas campos de bits, podemos representar esa misma estructura mucho ma:;
camprimida:
struct jecha
[
unsigned dia : 5;
unsigned mes : 4;
unsigned anyo: 7;
j;
Como ya se ha explicado anteriormente, podemos acceder a los miem-
bras individuales de la estructura utilizando el operador pun to, como se
ve en el siguiente ejemplo:
main( )
[
struct jecha
[
unsigned dia : 5;
unsigned mes : 4;
unsigned anyo: 7;
];
struct jecha hoy;
hoy.dia = 18; hoy.mes = 4; hoy.anyo = 91;
printj(C<%u/%u/%u  n': hoy.dia, hoy.mes, hoy.anyo);
]
Escribir un programa que de como resultado (en forma de tabla) las
frecuencias de parejas de letras adyacentes, de un texto introducido por el
teclado.
~ cual se contemplan todas las parejas posibles de letras, desde la aa
hasta la zz.
#include <stdio.h>
#include <stdlib.h>
#include <etype.h >
#dejine DIM ('z' - ca'
int tabla[DIM][DIM];
+ 1) / *jilas/columnas de la tabla */
/ * tabla de eontingencias */
main( )
I
char f, e;
char car;
char earant;
/ * earaeter actual */
/ * earaeter anterior */
/ * Poner tabla a eeros */
for if = 0; j < = cz' - ca'; j+ +)
for (e = 0; e < = cz' - ca'; e+ +)
tablaU][e] = 0;
system ("cls ");
printf("Introdueir texto. Finalizar con A Z.  n  n ");
carant = c';
while ((car = tolower(getehar( ))) != EOF)
I
if ((earant > = ca' && earant < = Cz') &&
(car> = ca' && car < = Cz'))
tabla[earant - ca'j[ear - ca'j
tabla[earant - ca'j[ear - ca'j + 1;
carant = car;
/ * Escribir tabla de jreeuencias */
system("cls ");
printf(" ");
for (c = ca'; e < = cz'; e+ +)
printf(" %e': c);
putchar('  n ');
putchar(f);
for (c = 'a:' c < = 'z'; c+ +)
printf(H%3d': tablaU - 'a'][c - 'a']);
putchart  n');
~ for if = 'a'; f < = 'z'; f+ +)
{
EI siguiente program a presenta en pantalla una ventana, rellenada con
el canicter tecleado y con posibilidad, a traves de un menu, de presentarla
en texto subrayado, en alta intensidad, parpadeando, en video inverso, nOf.
mal 0 con una combinacion de estas opciones.
Bajo MS-DOS, la memoria intermedia de pantalla con adaptador mo-
nocromo es de 4000 bytes, localizados a partir de la direccion 0 del seg-
mento OxBOOO.La memoria intermedia de pantalla con adaptador color
graficos (CGA) es de 4000 bytes de longitud en modo texto y de 16384 bytes
de longitud en modo gnifico, localizados, en ambos casos, a partir de la
direccion de memoria 0 del segmento OxB800.
EI atributo de representacion en pantalla se localiza en la parte alta
de la palabra y el canicter a representar, en la parte baja.
Notar que no se puede asignar a un campo de bits, un valor que exce-
da su capacidad. Por ejemplo, en el siguiente programa, el valor maximo
para el campo de 3 bits primer_plano, es 7 (111 en binario).
# include < stdio.h >
# include < stdlib.h >
# include <ctype.h >
Y Las siguientes constantes definen la ventana a utilizar
* dentro de la pantalla
*/
#define IFILA 11 / *primera fila de la ventana */
#define SFILA 20 / * ultima fila */
#define ICOLN 21 / *primera columna */
#define DCOLN 60 h ultima columna */
void escribir(char car, char atributo);
main( )
[
struct atributos
[
unsigned int primer ---plano
unsigned int intensidad
unsigned int color-Jondo
unsigned int parpadeo
J;
struct atributos atributo;
char ~trib = (char *)&atributo:
char car;
system("cls n);
printft'Escriba:
printj("
printj("
printf("
printf("
printf("
: 3; h bits 0 a 2 */
: 1; / * bit 3 */
: 3; h bits 4 a 6 */
: 1; / * bits 7 */
's' -- > subrayado,  nn);
'i' -- > int~nsidad,  nn);
'p' -- > parpadeo,  n n);
'v' -- > vldeo inverso,  nn);
'n' -- > vuelta a normal,  nn);
'x' -- > salir.  nn);
car = getch( );
while (tolower(car) != 'x')
[
switch (car)
[ .
case's':
atributo.primer ---plano
break;
case T:
atributo.intensidad
break;
/ * subrayado */
1;
/ * alta intensidad d
(atributo.intensidad = = 1) ? 0 : 1;
case 'p':
atributo.parpadeo
break;
case 'v': / * v(deo inverso */
atriblJto.primer ~lano 0;
atributo.color -fondo = 7;
break;
case 'n':
/ * parpadeo */
(atributo.parpadeo = = 1) ? 0 : 1;
atributo.primer ~lano 7;
atributo.color -fondo = 0;
break;
l
escribir(car, ~trib);
car = getch( );
Rellenar la pantalla con el caracter car
**************************************************************/
void escribir(char car, char atributo)
{
iot far *p;
iot fila, col;
p = (iot far *) OxB8000000; / * asignar a p B800:0000 */
for (fila = IFILA; fila < = SFILA; fila + +)
for (col = ICOLN; col < = DCOLN; col+ +)
4p + fila * COLS + col) = car I atributo < < 8;
En este programa, la estructura atributo contiene los atributos de los
caracteres a representar en la ventana. Estos atributos tienen que localizar-
se en la parte alta de la palabra de memoria correspondiente al canicter
a representar. De ahi, el ejecutar la sentencia:
la ~almacena en la palabra de direcci6np+jila*COLS+col, el canlcter
en la parte baja y los atributos en la parte alta. La variable p (puntero)
contiene la direcci6n de comienzo de la pantalla, esto es, la direcci6n co-
rrespondiente a la fila 1 columna 1.
Para acceder a una direcci6n fuera del segmento de datos en el cual
estamos trabajando, utilizamos direcciones segmentadas. Para asignar un
valor hexadecimal como una direcci6n segmentada, utilizaremos la pala-
bra clave far. Como ejemplo, observar la sentencia:
la cual indica, que la funci6n escribir debe recibir dos valores de tipo char:
cary atributo. El argumento atributo en esta funci6n es de tipo entero, por-
que necesitamos realizar sobre el un desplazamiento. Por ello, la llamada
a la funci6n escribir tendrfa que ser de la forma:
la cual da lugar a un error, ya que atributo en la funci6n main( ) es una
estructura.
Para salvar este inconveniente, recordar que un objeto de un determi-
nado tipo, puede ser convertido a otro tipo cualquiera, utilizando conver-
siones explicitas de tipo sobre punteros (ver Conversion explicita del tipo
de una expresion, en el capitulo 2). De ahi, la instrucci6n:
~ / ** .•******** Manipulacion de un valor float bit a bit ***********
main( )
{
struct sfl
{
unsigned bO : 1; unsigned b1 : 1;
unsigned b2 : 1; unsigned b3 : 1;
unsigned b4 : 1; unsigned b5 : 1;
unsigned b6 : 1; unsigned b 7 : 1;
unsigned b8 : 1; unsigned b9 : 1;
unsigned b10: 1; unsigned bll: 1;
unsigned b12: 1; unsigned b13: 1;
unsigned b14: 1; unsigned b15: 1;
unsigned b16: 1; unsigned b17: 1;
unsigned b18: 1; unsigned b19: 1;
unsigned b20: 1; unsigned b21: 1;
unsigned b22: 1; unsigned b23: 1;
unsigned b24: 1; unsigned b25: 1;
unsigned b26: 1; unsigned b27: 1;
unsigned b28: 1; unsigned b29: 1;
unsigned b30: 1; unsigned b31: 1;
};
union ufl
{
float x;
struct sfl s;
};
real.x = -10.5;
printj((real = ");
printj((%d': real.s.b31); printf((%d': real.s.b30);
printf((O/Od': real.s.b29); printft'%d': real.s.b28);
printft'%d': real.s.b27); printj((%d': real.s.b26);
printjt'%d': real.s.b25); printj((O/Od': real.s.b24);
printjt'%d': real.s.b23); printj((%d': real.s.b22);
printj2.'%d': real.s.b2I);printj("%d': real.s.b20);
print]("%d': real.s.bI9);printf("%d': real.s.bI8);
printj("%d': real.s.b17);printf("%d': real.s.bI6);
printj("%d': real.s.bI5);printj("%d': real.s.bI4);
printj("%d': real.s.bI3);printj("%d': real.s.b12);
printf("%d': real.s.bll);printj("%d': real.s.bIO);
printf("%d': real.s.b9); printj("%d': real.s.b8);
printf("%d': real.s.b7); printj("%d': real.s.b6);
printf("%d': real.s.b5); printj("%d': real.s.b4);
printf("%d': real.s.b3); printj("%d': real.s.b2);
printj("%d': real.s.bI ); printj("%d  n': real.s.bO);
)
Signa: 1 S = 1
Exponente: 100 0001 0 E = 3
Mantisa: 010 10000000000000000000 M = 0.3125
Este resultado esta representado en coma flotante, bajo el formate es-
tandar IEEE; el cual emplea, mantisa fraccionaria normalizada en signa
y magnitud, y sin almacenar el bit impHcito que es igual 1. EI exponente
esta representado en exceso 127.
Par tanto, el valor viene dado por (-1)SX l.Mx2E-127 para 0<E<255.
Aplicado a nuestro ejemplo, obtenemos:
Un puntero es una variable que contiene la direccion de memoria, de un
dato 0 de otra variable que contiene al dato. Quiero esto decir, que el pun-
tero apunta al espacio ffsico donde esta el dato 0 la variable. Un puntero
puede apuntar a un objeto de cualquier tipo, incluyendo estructuras, fun-
ciones etc. Los punteros se pueden utilizar para crear y manipular estruc-
turas de datos, para asignar memoria dinamicamente y para proveer el paso
de argumentos por referencia en las llamadas a funciones.
Un puntero se declara precediendo el identificador que referencia al pun-
tero, por el operador de indireccion (*), el cual significa "puntero a". Un
puntero siempre apunta a un objeto de un tipo particular. Un puntero no
inicializado tiene un valor desconocido.
Especifica el tipo del objeto apuntado; puede ser cualquier
tipo, incluyendo tipos agregados.
var-pun~ro
int *pint;
char *pnom;
double *p, *-Cj;
/ *pint es un puntero a un entero */
/ * pnom es un puntero a una cadena de caracteres*/
/ *p y q son punteros a reales */
EI espacio de memoria requerido para un puntero, es el numero de
bytes necesarios para especificar una direccion maquina. En la familia de
micros 8086, una direccion near (direccion con respecto a la direccion base
del segmento) necesita 16 bits y una direccion far (direccion segmentada)
necesita 32 bits.
operador direccion de : &
operador de indireccion: *
El operador unitario &, devuelve como resultado la direccion de su
operando.
EI operador unitario *, toma su operando como una direccion y nos
da como resultado su contenido.
# include <stdio.h>
main( )
{
/ * declaramos las variables enteras a, b y los punteros p y q
* a enteros
*/
int a = 10, b, *P, *q;
q = &a; / * asigna la direcci6n de a a la variable q */
/ * q apunta a la variable entera a */
b ~ *']; 1* asigna a b el contenido de la direccion q */
*P = 20; / * error: asignacion a un puntero no valida */
/ * ~ a donde apunta p ? */
printj(C<Enla direccion %.4X esta el dato %d  n': q, b);
printf(C<Enla direccion %.4X esta el dato %d n': p, *p);
}
En la direccion OD96 esta el dato 10
En la direccion 54E6 esta el dato 20
~ Como sabe C cuantos bytes tiene que asignar a una variable desde una
direccion ? La respuesta es que C toma como referencia el tipo base del
puntero y asigna el numero de bytes correspondientes a ese tipo.
# include <stdio.h >
main( )
[
float a = 10.5, b = 0; / * a y b son variables de tipo real */
p = &a;
b=*p;
printft'b
l
Al compilar este programa, se nos presentara el mensaje siguiente, de-
bido a la sentencia: p = &a;
OPER~IONES CON PUNTEROS
# include <stdio.h >
main( )
(
int a = 10, *P, *q;
p = &a;
q = p; / * la direcci6n que contiene p se asigna a q */
printf("En la direcci6n %.4X estd el valor %d': q, *q);
}
int *P;
p++;
p--;
p = p + 3;
p = p - 3;
/ * declam p como un puntero a un entero */
/ * hace que p apunte al siguiente entero */
/ * hace que p apunte al entero anterior */
/ * avanzar tres enteros */
/ * retroceder tres enteros */
Si p y q son variables tipo puntero y apuntan a elementos del mismo
array, la operaci6n p - q es valida. No se permite sumar, multiplicar, divi-
dir 0 rotar punteros y tampoco sumarles un real.
Es posible comparar dos punteros en una expresion de relacion. Esta ope-
racion tiene sentido si ambos punteros apuntan a elementos del mismo array.
if (p + n < = q)
P += n;
if (q /= NULL && q < = p)
q++;
Los operadores unitarios * y & tienen mayor precedencia que los operado-
res aritmeticos + y - e igual precedencia que + + Y --.
Definimos dos variables enteras a y b, y dos punteros a datos de tipo
entero pa y pb.
al valor apuntado por pa se Ie suma 1, y el resultado
se asigna a b.
el siguiente entero al entero apuntado por pa, es asig-
nado a b.
la variable b se decrementa en una unidad. Sin paren-
tesis, se decrementaria pb y no b.
Un ejemplo interesante de punteros es un examen del contenido de la
memoria. El siguiente programa permite visualizar byte a byte el conteni·
do de la memoria RAM, a partir de una posici6n dada.
Este programa introduce la palabra clavefar la cual nos proporciona
una direcci6n segmentada, con la finalidad de poder acceder a posiciones
de memoria fuera del segmento en el que nos encontramos.
# include <stdio.h>
# include <stdfib.h>
# include <etype.h >
# include < dos.h >
main( ) / *funcion principal */
[
unsigned long de; / * direecion de eomienzo */
void explorar(unsigned long);
system("cls' ');
printf("Introducir la direecion de eomienzo ");
seanf("%lx': &de); / *por ejemplo B8000000 */
explorar(de); / * llamada a la funcion explorar */
}
void explorar(unsigned long de)
[
unsigned char far *p;
/ *p eontiene una direecion segmentada, a un byte */
int e, fin;
char r;
p = (unsigned char far *)de;
/ * La notacion cast: (unsigned char far *) eonvierte el valor
* de de a una direecion segmentada
*/
do
(
system("cls");
for (lin = 1; !in < = 16; !in+ +) / *pdginas de 16 lfneas */
(
printj("%04X'%04X ': FP_SEG(p), FP_OFF(p));
for (c = 1; c < = 16; c+ +) / * escribir 16 bytes */
printj("%02X ': *p+ +); / * escribir el contenido de p */
putchar{'  n '); / * cambio de {{nea */
J
printj("  n  nPulse una tecla para continuar. S para sa!ir ");
r = getch( );
J
while (tolower(r) 1= 's'); / * jina!izar la exploraci6n cuando
se pulse una tecla */
El programa utiliza una variable dc, de tipo unsigned long ya que una
direcci6n de memoria excede del tamafio de un entero. Observar tambien
el formato %lx que se utiliza con la funci6n scanf( ), para leer un entero
formato largo en hexadecimal. Tambien utiliza un puntero p a un objeto
de tipo unsigned char ya que el rango de valores para este tipo es de 0 a 255.
Las funciones FP_SEG(p) y FP_OFF(p) nos dan como resultado la
direcci6n del segmento y la direcci6n offset dentro de este, de la direcci6n
far p.
Esto permite, utilizando la notaci6n cast, asignar a la variable p, un
puntero de cualquier tipo. Esto es a menu do utilizado en el contexto de
llamadas a funciones.
En C existe, entre punteros y arrays, una relaci6n tal que, cualquier opera-
ci6n que se pueda realizar mediante la indexaci6n de un array, se puede
realizar tambien con punteros.
Para clarificar 10 expuesto, observar el siguiente programa, realizado
primeramente con arrays y a continuaci6n con punteros.
# include <stdio.h>
main( )
{
static int lista[ J = {24, 30, 15, 45, 34};
int ind;
for (ind = 0; ind < 5; ind+ +)
printf(H%d ': lista[ind]);
En este ejemplo, la notaci6n utilizada para acceder a los elementos
del array estatico, es la expresi6n: lista!indj.
# include <stdio.h>
main( )
{
static int lista! J {24, 30, 15, 45, 34};
int ind;
for (ind = 0; ind < 5; ind+ +)
printf(H%d ': *(lista+ind));
Esta versi6n es identica a la anterior, excepto que la expresi6n pa
acceder a los elementos del array es: *(lista+ind).
Esto deja constancia de que /ista es la direccion de comienzo del array.
Si a esta direccion Ie sumamos 1, 0 dicho de otra manera si ind vale 1, nos
situamos en el siguiente entero de la lista; esto es *(/ista +l) y /ista[l] repre-
sentan el mismo valor. Notar que un incremento de uno sobre una direc-
cion equivale a avanzar no un byte, sino un numero de bytes igual al tama-
no de los objetos que estamos tratando.
Segun esto, hay dos formas de referirnos al contenido de un elemento
de un array:
indica la posicion de un elemento dentro del array. EI primer ele-
mento ocupa la posicion 0 y el ultimo la posicion 0-1, siendo 0
el numero total de elementos.
Puesto que *(/ista +0) es igual que /ista[O], la asignacion: p = &/ista[O]
es la misma que la asignacion p = /ista, donde pes una variable de tipo
puntero. Esto indica que la direccion de comienzo de un array es la misma
que la del primer elemento. Por otra parte, despues de haber efectuado la
asignacion p = /ista, las siguientes expresiones dan lugar a identicos re-
sultados:
Sin embargo, hay una diferencia entre el nombre de un array y un pun-
tero. EI nombre de un array es una constante y un puntero es una variable.
Esto quiere decir que las operaciones:
!isla = p 0 lista+ +
p = /ista 0 P+ +
no son correctas, y las operaciones
sf son correctas.
Puesto que una cadena de caracteres es un array de caracteres, es correcto
pensar que la teorfa expuesta anteriormente, es perfectamente aplicable a
cadenas de caracteres.
char *nombre = "Francisco Javier";
printj("%s': nombre);
Este ejemplo asigna a nombre, la direccion de comienzo de la cadena
de caracteres especificada. EI compilador afiade al final de la cadena, el
canicter  0, para que en operaciones posteriores podamos detectar el fi-
nal de la misma.
Muchas funciones de C que trabajan con cadenas de caracteres, utili-
zan punteros y devuelven como resultado un puntero. Por ejemplo, la fun-
cion de C strchr( ) para localizar un canicter en una cadena, devuelve un
puntero al canicter buscado 0 un puntero nulo si el canicter no se encuentra.
EI siguiente ejemplo estudia posibles formas de calcular la longitud
de una cadena de caracteres, utilizando punteros.
main( )
[
static char *cadena = "abcd"; / * el cardcter de terminaci6n
' 0' es afiadido automdticamente */
printj("%d  n': longstr(cadena));
l
int longstr(char *-Str)
I
char *P = str;
while (*P 1= t  0')
P++;
return ((int)(p - str));
]
La diferencia p - str nos da la longitud de la cadena. El bucle formado
par la sentencia while, realiza las siguientes operaciones:
3. Si no es el canlcter nulo, se pasa a la siguiente direcci6n y se repite
el proceso desde el punta 1.
Puesto que la sentencia while s610 comprueba si la expresi6n *p 1= t  0'
es cera, en lugar de hacer la comparaci6n explfcitamente, se puede hacer
implicitamente as!:
while (*p)
P++;
Ahara la condici6n est a formada por la expresi6n *p. Si esta expre-
si6n es distinta de cero, la condici6n es cierta. En cambio si es cero, (carac-
ter nula, ya que su valor ASCII es 0) la condici6n es falsa.
Ahara el resultado vendra dado por la expresi6n p - str - 1, ya que
despues de examinar si la condici6n es cero, se efectua la operaci6n + + .
Teneren cuenta que si escribimos * + +p, primero se efectua la operaci6n
+ + sabre p y despues se examina el contenido de esa direcci6n. Esto no
seria valida para cadenas de longitud 1.
Siendo c una variable de tipo char,
la expresion es la misma que y es equivalente a
)
c *P+ +,o c *(P+ +),o c = *P,op+ +,o
c *+ +P,o c *(+ +p),o + +P,oc = *P,o
c ++ *P,o c + + (*p),o *p+=l,oc= *P,
c = (*p) + +,o c = *p,o(*p) + +;
El siguiente programa copia una cadena de caractereS en otra. Prime-
ramente se presenta una version con arrays y despues otra con punteros.
main( )
(
char cadenal[81], cadena2[81],o
void copstr(char *, char *),o
printft'Introducir cadena: "),o
gets(cadenal),o
copstr(cadena2, cadenal),o / * copia fa cadenal en fa cadena2 d
printf(H  n  nLa cadena copiada es: %s  n': cadena2),o
l
void copstr(char *P, char~) / * copia q en p */
{
int i = 0;
while ((p[i]
i+ +,o
void copstr(char *p, char..q) / * copia q en p */
[
while ((*p = ..q) != < OJ
[
p++;
q++;
J
J
Si tras1adamos e1incremento de p y q a 1a expresi6n de 1a sentencia
while, obtenemos esta otra versi6n:
void copstr(char *P, char..q) / * copia q en p */
[
while ((*P+ + = ..q+ +)!= < 0');
J
E1bucle formado por 1asenten cia while, realiza 1assiguientes opera-
ciones:
1. Toma e1canicter contenido en 1adirecci6n dad a por q y 10copia
en 1a direcci6n dada por p.
3. p pasa a apuntar a 1asiguiente direcci6n, y 10mismo sucede con
q. Si no se dio 1a condici6n de terminaci6n, e1proceso se repite
desde e1punta 1.
Puesto que 1asentencia while s610comprueba si 1aexpresi6n *P != <  0'
es cera, en 1ugar de hacer 1a comparaci6n explicitamente, se puede hacer
implicitamente asi:
void copstr(char *P, char..q) / * copia q en p */
[
while (*P + + = ..q+ +);
J
Cuando se pasa el nombre de un array a una funci6n, se pasa la direc-
ci6n de comienzo del array, ya que el nombre del array es un puntero a
dicho array; dicho de otra forma el paso de un array se hace por referencia
y no por valor.
Para hacer referencia a una cadena de caracteres, podemos utilizar un pun-
tero 0 un array.
Cuando se utiliza un puntero, podemos inicializar la variable de la for-
ma siguiente:
La version array asigna el numero suficiente de bytes para almacenar
la cadena especificada, (en este caso 16) mas un byte para el carcicter de
terminaci6n '  0'. La direcci6n del primer caracter del array viene dada
por el nombre del array; en este caso, por nombre.
En la version puntero, la asignaci6n de bytes se efectua de la misma
forma, pero ademas se asigna memoria para la variable puntero nombre,
que es la que contiene la direcci6n de comienzo de la cadena. Esto quiere
decir que con esta versi6n se ocupa mas memoria, pero tambien las venta-
jas son mayores.
En la versi6n array, nombre es un puntero constante; por 10 tanto, la
direcci6n no puede ser modificada. En la versi6n puntero, nombre es una
variable, por 10 que sf puede ser modificada. Esto es, admite operaciones
como + + nombre, prohibida en la versi6n array.
En capitulos anteriores, hemos trabajado con arrays multidimensionales,
aunque en la practica 10 mas habitual es trabajar con arrays de punteros,
cuesti6n que se expone a continuaci6n.
Se puede definir un array, para que sus elementos contengan en lugar
de un dato, una direcci6n 0 puntero.
int *a[lOj, v;
a[O] = &v;
prin!f("%d': *-fl[O]);
Este ejemplo define un array de 10 elementos que son punteros a da-
tos de tipo in! y una variable entera v. A continuaci6n asigna al elemento
a[O], la direcci6n de v y escribe su contenido.
Cuando cada elemento de un array es un puntero a otro array, esta-
mos en el caso de una doble indirecci6n 0 punteros a punteros. La caracte-
ristica de poder referenciar un puntero con otro puntero, Ie da allenguaje
C una gran potencia y flexibilidad para crear estructuras complejas.
Un array de dos dimensiones y un array de punteros, se pueden utili·
zar de forma parecida, pero no son 10 mismo.
int a[lO][lOj;
int *b[lOj;
Este ejemplo define un array a de 100 elementos de tipo entero y un
array b de 10 elementos declarados como punteros a objetos de tipo ente-
rooSuponiendo que cada uno de estos objetos es un array de 10 elementos,
la ocupaci6n de memoria sera de 100 elementos mas la de los 10 elementos
de b.
Las ventajas que tiene el array b sobre el a, es que el acceso a un ele-
mento se efectua mediante una indirecci6n a traves de un puntero, en lugar
de hacerlo mediante una multiplicacion y una suma, y que los arrays apun·
tados pueden ser de longitud variable.
Para especificar que una variable es un puntero a un puntero, se pro·
cede de la forma siguiente:
El siguiente programa multiplica los elementos de un array de dos di·
mensiones por una constante y a continuacion escribe el contenido de di·
cho array. Primeramente se presenta una version con arrays y despues otra
con punteros.
# include <stdio.h>
# define F 4 / * nzimero de fi/as */
#define C 5 / * nzimero de eolumnas */
main( )
{
static int tabla[F][C] { { 10, 12, 15, 17, 14 },
{ 22, 20, 23, 25, 21 1
{38, 30, 34, 36, 35 },
{ 45, 41, 44, 48, 49 } };
const int k
int f, e;
/ * Multipliear eada elemento por una eonstante k */
for (f = 0; f < F; f+ +)
for (e = 0; e < C; e+ +)
tablaUI[e] * = k;
/ * Escribir el array */
for if = 0; f < F; f+ +)
(
for (c = 0; c < c; c+ +)
printft'%5d'~ tablaU][cJ);
putchart  n ');
I
I
Para reescribir este programa utilizando la notaci6n de punteros en
lugar de la notaci6n array, nos planteamos unicamente la cuesti6n de c6mo
escribir la expresi6n tablaU][c], utilizando la notaci6n de punteros. Pues
bien, pensemos que un array de dos dimensiones es un array de una di-
mension, donde cada elemento es a su vez un array de una dimensi6n.
En el ejemplo anterior, la direcci6n de to do el array es tabla. Este array
tiene 4 elementos (filas), tabla!O] a tabla!3]. La direcci6n de cada elemento
fila (array de 5 elementos) es:
Si elegimos una fila, por ejemplo tabla!1], 0 en notaci6n puntero
tabla + 1, interpretamos esta expresi6n como un puntero a un array de 5
elementos; esto qui ere decir que tabla +1 es un puntero a un puntero, 0 que
el contenido de tabla +1, *(tabla +1), es la direcci6n del primer elemento
de esa fila, tabla!1][O], 0 en notaci6n puntero *(tabla +1)+O. Las direccio-
nes tabla +1 y *(tabla +1) coinciden, pero ambas expresiones tienen dife-
rente significado. Por ejemplo:
tabla +1+2
*(tabla+1) +2
4*(tabla+ 1)+2)
se refiere a la fila tabla!3] y
se refiere al elemento tabla!1][2]
es el contenido del elemento tabla!1][2].
De acuerdo con 10 expuesto la versi6n con punteros del ejemplo ante-
rior, presenta solamente la siguiente modificaci6n:
/ * Escribir ef array */
for if = 0; j < F; j+ +)
{
for (c = 0; c < c; c+ +)
printj("%5d': *(*(tabfa + j) + c));
putchar('  n ');
l
Vamos a estudiar mediante un ejemplo, como inicializar un array de pun-
teros a cadenas de caracteres. Consideremos el problema de escribir una
funcion para que recibiendo un numero entero correspondiente a un mes,
nos devuelva como resultado un puntero a una cadena de caracteres que
corresponda al nombre de dicho meso
# include <stdio.h>
main( )
(
unsigned int dia, mes, anyo;
char *m;
char *nombre_mes(unsigned int mm);
printj("Introduce jecha (dd-mm-aaaa): ");
/ * Los datos en fa entrada iran separados por '-' */
scanf("%u-%u-%u': &dia, &mes, &anyo);
m = nombre_mes(mes);
printj("  n  nMes: %s  n': m);
l
char Mombre_mes(unsigned int mm)
{
/ * mes es un array de punteros a cadenas de caracteres */
static char *mes[ ] = { "Mes no correcto':
"Enero': "Pebrero': "Marzo':
"Abril': "Mayo': "Junio': "Julio':
':4gosto': HSeptiembre': HOctubre':
HNoviembre': HDiciembre" J;
return ((mm > 0 && mm < = 12) ? mes[mmj : mes[OJ);
J
En este ejemplo, mes[ j es un array de 13 elementos (0 a 12) que son
punteros a cadenas de caracteres. Estas, como se ve, son de diferente longi-
tud y todas son finalizadas automaticamente con el caracter nulo ( 0).
Si en lugar de utilizar un array de punteros, hubieramos utilizado un array
de dos dimensiones, el numero de columnas seria la de la cadena mas lar-
ga, mas uno para el caracter nulo, con 10 que la ocupaci6n de memoria
seria mayor.
El siguiente ejemplo muestra un programa para clasificar cadenas de
caracteres por orden alfabetico.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NMAX 25
#define CMAX 81
/ * mimero maximo de cadenas */
/ * mimero maximo de caracteres por cadena */
main( ) / *fun cion principal */
[
char cadena[NMAXj[CMAXj;
char *pcadena[NMAXj;
int ncadenas;
/ * array de cadenas */
/ * array de punteros a cadenas d
/ * numero de cadenas lefdas */
int LeerCadena(char cadena[ j[CMAXj, char *pcadena[ j, iut nmc);
void clasijicar(char *pcadena[ j, iut nc);
void escribir(char *pcadena[ j, iut nc);
systemt 'els");
printj(UClasijicacion de cadenas de caracteres.  n");
printj(Ulntroducir cadenas a elasijicar. Enter para salir.  n ");
if ((ncadenas = LeerCadena(cadena, pcadena, NMAX)) > 0)
I
printft'Proceso de elasijicacion.  n  n");
clasijicar(pcadena, ncadenas);
escribir(pcadena, ncadenas);
l
else
printf("Cero 0 demasiadas cadenas para clasijicar  n");
int LeerCadena(char cadena[ j[CMAXJ, char *pcadena[ J, int nmc)
{
/ * nmc = numero maximo de cadenas */
int longitud, ncadenas = 0;
while ((longitud = strlen(gets(cadena[ncadenasJ))) > 0)
{
if (ncadenas > = nmc)
return (-1); / * demasiadas cadenas a ordenar */
else
/ * guardar el apuntador a la cadena en el array */
pcadena[ncadenas+ +J = cadena[ncadenasJ;
l
return (ncadenas); / * numero de cadenas lefdas */
l
/**************************************************************
Funci6n clasijicar
**************************************************************/
/ * Ordena las cadenas pcadena[OJ ... pcadena[nc - 1J
ascendentemente. nc = numero de cadenas a ordenar */
void clasijicar(char *pcadena[ J, int nc)
{
char ~W(; / *puntero auxiliar */
int i, s = 1;
while ((s = 1) && (--nc > 0))
{
s = 0; I. no permutaci6n .1
for (i = 1; i < = nc; i+ +)
if (strcmp(pcadena[i-1J, pcadena[i]) > 0)
{
aux = pcadena[i-1J;
pcadena[i-1J = pcadena[iJ;
pcadena[iJ = aux;
s = 1; I. permutaci6n .1
}
}
}
1**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
Funci6n escribir
**.**••••••••••••••••••••••••••••••••••••••••••••••••••••••• **1
void escribir(char .pcadena[ J, iot nc)
{
1* nc = mimero de cadenas a escribir .1
while (--nc > = 0)
printj(H%s  n': .pcadena + +);
El programa utiliza arrays de caracteres y punteros a cadenas de ca-
racteres. La clasificacion y, por consiguiente, la escritura del resultado, se
realiza sobre el array de punteros, ya que resulta mas facH.
Existen dos metodos fundamentales que C utiliza para almacenar infor-
macion en la memoria. El primero utiliza variables globales y locales. En
el caso de variables globales, el espacio es fijado y utilizado a 10 largo de
toda la ejecucion del programa (~ATA); yen el caso de variables loca-
les, la asignacion se hace a traves del stack.
El segundo metodo utiliza funciones predefinidas en C, como mal/ocr )
y free(). Como es logico, estas funciones utilizan el area de memoria libre
(Heap), para .realizar las asignaciones de memoria.
ROM BIOS
Extensiones
BASIC y BIOS
Memoria para video
Programa
DOS
Heap
Stack
_DATA
_TEXT
La asignacion dimimiCa de memoria consiste en asignar la cantidad
de memoria necesaria para almacenar un objeto durante la ejecucion del
programa, en vez de hacerlo en el momento de la compilacion del mismo.
Cuando se asigna memoria para un objeto de un tipo cualquiera, se de-
vuelve un puntero a la zona de memoria asignada.
La funci6n mal/oc devuelve un puntero que referencia el espacio asig-
nado, a un objeto de un tipo no especificado. Si hay insuficiente espacio
de memoria 0 si t es 0, la funci6n retorna un puntero nulo (NULL).
El espacio puede ser asignado a cualquier tipo de objeto. Para realizar
----""laconversi6n al tipo deseado, utilizar la notaci6n cast sobre el valor devuelto.
El siguiente programa asigna espacio para cadenas de caracteres. Los
punteros a cada cadena son guardados en un array de punteros.
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define NML 100 / * numero maximo de lfneas */
main( )
(
char *plinea[NML]; / * array de punteros alas cadenas */
char *p, linea[81];
iot i, longitud, nlineas = 0;
system (Hcls");
printf(Hlntroducir cadenas de caracteres.  n");
printf(HFinalizar con Enter.  n  n");
while ((longitud = strlen(gets(linea))) > 0 && nlineas < NML)
(
/ *Asignar espacio para una cadena de caracteres */
p = (char *)mal/oc(longitud +1);
if (p = = NULL)
(
printft'Insujiciente espacio de memoria disponible  n");
exit(l); * terminar el proceso */
J
else
[
/ * copiar la cadena en el espacio de memoria asignado */
strcpy(p, linea);
/ * guardar el puntero a la cadena en el array */
plinea[nlineas+ +J = p;
}
}
/ * Escribir las cadenas almacenadas */
system("cls ");
printj("Lfneas almacenadas:  n  n ");
for (i = 0; i < nlineas; i+ +)
printj("%s  n': plinea[i]);
La sentencia: p = (char *)mal/oc(longitud +1); asigna un espacio de
memoria de longitud +1 bytes, para una cadena de caracteres. EI espacio
de memoria es referenciado por e1 puntero p, el cual ha sido declarado como
un puntero a una cadena de caracteres. Recordar que la funci6n srtlen()
no contabiliza el canicter nulo de terminaci6n; de ahi la expresi6n
longitud +1.
Utilizando la funci6n mal/ocr ) y otras que se presentan a continuaci6n,
es posible definir arrays en tiempo de ejecuci6n, denominados tambien arrays
dimimicos.
scanj("O/Od': &n_elementos);
a = (iot *)mal/oc(n_elementos * sizeof(iot));
for (i = 0; i < n_elementos; i+ +)
scan!("O/Od': &a[i]);
Esta funci6n asigna espacio de memoria para un array de n elementos, de
longitud t bytes cada uno de ellos. Cada elemento es inicializado a o.
La funci6n calloc( ) devuelve un puntero al espacio asignado. Este es-
pacio puede ser asignado a un array de cualquier tipo. Para ello, utilizar
la notaci6n cast sobre el valor devuelto.
Si hay insuficiente espacio de memoria, 0 si not son 0, la funci6n
retorna un puntero nulo (NULL). EI espacio de memoria requerido debe
ser inferior a 64K.
EI siguiente programa asigna espacio para un array de N elementos
de tipo entero.
# include <stdio.h>
# include <stdlib.h >
const int N = 100; / * numero maximo de elementos para el array :~/
main( )
[
int i = 0;
/ * Asignar espacio para N enteros */
!ista = (int *)calloc(N, sizeof(int));
if (lista = = NULL)
printj(Hlnsuficiente espacio de memoria disponible  n");
else
printf(HEspacio de memoria asignado para N enteros  n  n");
lista[9] = 100;
~ while (i+ + < N)
printj(H%8d': *lista+ +);
EI puntero devuelto por la funci6n cal/oc( ) es convertido mediante
la notaci6n cast (int *), para que apunte a objetos de tipo entero y almace-
nado en la variable lista.
Esta funci6n cambia el tamafio de un bloque de memoria previamente asig-
nado. EI argumento p es un puntero que apunta al comienzo del bloque.
Si p es NULL, esta funci6n se comporta igual que mal/ocr ) y asigna un
nuevo bloque de t bytes. Si p no es nulo, entonces tiene que ser un puntero
devuelto por las funciones mal/ocr ), cal/oc( ) 0 por la propia funci6n rea-
l/oc( ). EI bloque ha po dido, incluso, ser liberado por la funci6n free().
EI argumento t, da el nuevo tamafio del bloque en bytes. EI contenido del
bloque no cambia en el espacio conservado.
La funci6n real/ocr) devuelve un puntero al espacio asignado. EI blo-
que puede ser movido al modificar el tamafio, esto quiere decir que p pue·
de cambiar. Este espacio puede ser asignado a un objeto de cualquier tipo.
Para ello, utilizar la notaci6n cast sobre el valor devuelto.
Si hay insuficiente espacio de memoria 0 si t es 0, la funci6n retorna
un puntero nulo (NULL). Cuando esto ocurre, el bloque original es liberado.
Esta funci6n libera un bloque de memoria asignado por las funciones ma-
lloc(), calloc( ) 0 realloc( ). Un puntero nulo es ignorado.
EI siguiente programa muestra c6mo realizar una reasignaci6n de me-
moria; y pone de manifiesto que despues de una reasignaci6n, la informa-
cionno varia en el espacio de memoria conservado. Por ultimo, el bloque
de memoria es liberado.
# include <stdio.h >
# include <stdlib.h>
# include <string.h>
const int BYTES = 40;
main()
I
/ * asignar espacio para una cadena de caracteres */
p = malloc(BYTES * sizeof(char));
/ * asignar cadena de caracteres */
strcpy(p, "abcdef 0");
/ * reasignar el bloque para contener mas caracteres */
if (p != NULL)
p = realloc(p, BYTES *2 * sizeof(char));
if (p != NULL)
(
printf(HBloque reasignado  n");
/ * Escribir la cadena original */
printft'%s  n': p);
/ * Liberar el espacio de memoria */
jree(p);
printf(H  nEI bloque ha sido liberado  n");
}
else
(
printf(HLa reasignaci6n no ha sido posible  n");
printf(HEI espacio ocupado por el bloque ha sido liberado");
}
}
Esta funcion asigna espacio de memoria para un array de n elementos, de
longitud t bytes cada uno de ellos. EI espacio total de memoria puede ser
mas grande que 64K (array huge).
La funcion halloc( ), devuelve un puntero que referencia el espacio
asignado.
EI espacio puede ser asignado a cualquier tipo de objeto. Para ello rea·
lizaremos una conversion explicita sobre el valor retornado, utilizando la
notacion cast.
Esta funcion libera un bloque de memoria asignado por la funcion haUoen
#include <malloc.h>
void hfree(void _huge *p);
Lospunteros a estructuras se declaran igual que los punteros a otros tipos
de datos. C utiliza el operador - > para referirse a un miembro de una es-
tructura apuntada por un puntero.
EI siguiente ejemplo declara un puntero hoy a una estructura, asigna
un valor a cada miembro de la misma y, apoyandose en una fundon, escri-
be su contenido.
# include < stdio.h >
# include < stdlib.h >
structjecha / * declaraci6n del tipo estructura jecha */
!
unsigned int dd,o
unsigned int mm,o
unsigned int aa,o
];
void escribir(struct jecha 4);
main()
!
struct jecha *hoy,o/ * hoy es un puntero a una estructura */
/ * asignaci6n de memoria para la estructura */
hoy = (struct jecha *)malloc(sizeof(struct jecha)),o
printj("Introducir jecha (dd-mm-aa): "),o
scanf("%u- %u- %u': &hoy- >dd, &hoy- >mm, &hoy- >aa);
escribir(hoy),o
]
void escribir(struct jecha 4)
!
printf("Dia %u del mes %u del ano %u  n': j- >dd, j- >mm, j- >aa);
]
Observar que el tipo struct jecha va definido fuera de toda funci6n,
para poder utilizarlo en cualquier parte y que mediante la fund6n mal/oc()
asignamos memoria para un objeto de tipo struct jecha.
Los punteros a uniones se manipulan exactamente igual que los pun-
teros a estructuras.
Una declaraci6n compleja es un identificador calificado por mas de un ope-
rador (array: [ ], puntero: *, 0 funci6n: ( ) ). Se pueden aplicar varias com.'
binaciones con estos operadores sobre un identificador; sin embargo, un
array no puede contener funciones en sus elementos y una fund6n no pu~-
de devolver como resultado un array 0 una funci6n.
Para interpretar estas declaradones, hay que saber que los corchetes
y parentesis (operadores a la derecha del identificador) tienen prioridad
sobre los asteriscos (operadores a la izquierda del identificador). Los pa-
rentesis y corchetes tienen la misma prioridad y se evaluan de izquierda
a derecha. Como ultimo paso se aplica el tipo especificado. Utilizando pa·
rentesis, podemos cambiar el orden de prioridades. Las expresiones entre
parentesis se evaluan primero, de mas internas a mas externas.
Una simple forma de interpretar declaraciones complejas es leerlas desde
dentro hacia afuera, de acuerdo con los siguientes pasos:
1. Comenzar con el identificador y mirar si hacia la derecha, hay cor-
chetes 0 parentesis.
2. Interpretar esos corchetes 0 parentesis y mirar si hacia la izquier-
da hay asteriscos.
3. Dentro de cada nivel de parentesis, de mas internos a mas exter-
nos, aplicar las reglas 1 y 2.
char *( *( *var) ( )) [10J
!I. !I. !I.!I. !I. !I. !I.
En este ejemplo se han numerado los pasos en el orden de interpreta-
cion, que es como sigue:
Nota: Para una informacion avanzada sobre punteros ver el capitulo titu-
lado "Manejo de la memoria".
Una funcion es una coleccion independiente de declaraciones y sentencias,
generalmente enfocadas a realizar una tarea especifica. Todo programa C
consta al menos de una funcion, la funcion main( ). Ademas de esta, pue-
dehaber otras funciones cuya finalidad es, fundamentalmente, descompo-
ner el problema general en subproblemas mas faciles de resolver y de man-
tener. La ejecucion de un programa comienza por la funcion main( ).
Cuando se llama a una funcion, el control se pasa a la misma para
su ejecucion; y cuando finaliza, el control es devuelto de nuevo al modulo
que llamo, para continuar con la ejecucion del mismo a partir de la sen-
tencia que efectuo la llamada.
main( )
[
func1();
func1();
J
func1( )
[
func2( )
[
La definici6n de una fund6n consta de la cabecera de funcion y del cuero
po de la funcion. La sintaxis correspondiente es:
[clase] [tipo] nombre([pardmetros-jormales]J
{
[declaraciones]
sentencias;
define el ambito de la funci6n, esto es, desde donde puede
ser Hamada. La clase puede ser: extern 0 static.
Una fund6n static es visible solamente en el fichero fuente
en el cual est a definida; y una fund6n extern es visible para
todos 10s ficheros fuente que componen el programa. Por
defecto, la clase de una funci6n es extern.
indica el tipo del valor devuelto por la funci6n. Puede ser
cualquier tipo fundamental 0 tipo definido por el usuario.
Por defecto, el tipo es into Una funci6n no puede retornar
un array 0 fund6n, pero si puede retornar un puntero a un
array 0 a una fund6n.
es un identificador que indica el nombre de la funci6n. Si
el nombre va precedido por el operador asterisco (*), el va-
lor devuelto por la funci6n es un puntero.
panimetros formales componen la lista de argumentos de la funci6n. Esta
lista consta de un conjunto de variables con sus tipos, sepa-
radas por comas y encerradas entre parentesis. Los panime-
tros formales son variables que reciben los valores pasados
en la Hamada a la funci6n. La sintaxis es la siguiente:
a parte del alrnacenamiento por defecto (auto), esta es la
unica clase de almacenamiento permitida para un parame-
tro formal. Una variable register se coloca en los registros
de la UCP, 10 cual da lugar a programas mas cortos y
rapidos.
indica el tipo del argumento, el cual puede ser un tipo fun-
damental, 0 un tipo definido por el usuario. Por defecto es
into
Sino se pasan argumentos a /a /Line.ion, /a usta de pa.afmetros JOrma-
les puede ser sustituida por la palabra clave void
Sies necesario, el compilador ejecuta las conversiones aritmeticas usua-
les sabre cada parametro formal y sobre cada argumento actual.
Este ejemplo define una funci6n Hamada suma, que acepta dos valo-
s enteros y retorna un valor entero.
Este ejemplo define una funci6n Hamada calculo, sin argumentos, la
cual retorna un valor real (double).
Esteejemplo define una funci6n Hamadajx, que acepta dos argumen-
s: a de tipo entero (int) y de clase de almacenamiento register y p que
un puntero a un valor de tipo char. EI resultado devuelto por la funci6n
un valor de tipo entero (int).
Este ejemplo define una funci6n Hamada suma, que acepta dos argu-
mentos, datol y dato2, de tipo entero (long). El valor retornado por la fun-
~ ci6n es un puntero a un valor de tipo entero (long).
Este ejemplo declara un puntero denominado suma, a una funci6n que
acepta dos argumentos, datol y dato2, de tipo entero (long) y que retorna
un valor tambien de tipo entero (long).
Este ejemplo define una funci6n Hamada dibujar, sin argumentos, la
cual no retorna un valor.
Este ejemplo define una funci6n multiplicar, que acepta dos argumentos
reales, datol y dato2, y retorna un valor que es un puntero a un array de
cinco elementos de tipo real.
Este ejemplo define una funci6n puntero, que acepta un argumento
p, declarado como un puntero a un objeto de tipo no especificado. La fun-
ci6n retorna un valor entero.
Este ejemplo define una funci6n jcalculo, que acepta dos argumen-
tos: n de tipo entero y pj que es un puntero a una funci6n que acepta un
argumento x entero y devuelve como resultado un valor de tipo long. La
funci6n jcalculo no retorna un valor.
La definici6n de una funci6n puede hacerse tambien, utilizando el for-
mato antiguo.
int suma(datal, data2)
int datal, data2;
(
[dec/aracianes]
sentencias;
l
int suma(int datal, int data2)
{
[dec/aracianes]
sentencias;
l
El cuerpo de una funcion esta formado por una senten cia compuesta que
contienesentendas que definen 10 que hace la fundon. Tambien puede con-
tener declaraciones de variables utilizadas en dichas sentendas. Estas va-
riables, por defecto, son locales a la fundon.
Hemos visto que cada funcion puede devolver un valor cuyo tipo se indica
enla cabecera de funcion. Este valor es devuelto a la sentenda de Hamada
ala fundon, por medio de la sentenda return, cuya sintaxis es la siguiente:
Si la sentenda return no se especifica 0 se especifica sin contener una
expresi6n, la fundon no devuelve un valor.
variable especifica la variable donde va a ser almacenado el valor de·
vuelto por la funcion. Notar que la llamada puede prescindir
del valor devuelto por la funcion.
expresion especifica una direccion que referencia a una funcion. Puede
ser, una expresion que es evaluada a una direccion de una fun·
cion, 0 simplemente un identificador que se corresponde con
el nombre de la funcion llamada. Esto significa que una fun·
cion puede ser llamada a traves de un puntero a una funci6n.
panimetros-actuales son una lista de expresiones separadas par comas. Las
expresiones son evaluadas y convertidas utilizando las conver-
siones aritmeticas usuales. Los valores resultantes son pasados
a la funcion y asignados a sus correspondientes panimetros fOf'
males. El numero de expresiones en la lista, debe ser igual al
numero de parametros formales, a no ser que se especifique un
numero variable de argumentos.
Este ejemplo llama a la funcion suma( ) y Ie pas a los valores de a y
b * 2. Si la funcion devuelve un valor, este no se tiene en cuenta.
Este ejemplo llama a la funcion multiplicar( ) y Ie pas a los valores de
a y b. La funcion devuelve un valor que es almacenado en la variable r.
Este ejemplo llama a la funci6n mayor( ) y Ie pasa los valores de v[i}
y de v[i+1] (elementos del array v). La funci6n devuelve un valor que es
almacenado en la variable n.
Cuando se ejecuta una llamada a una funci6n, el control es pasado
a la misma para su ejecuci6n. La ejecuci6n de la funci6n finaliza cuando
se ejecuta una sentencia return 0 cuando se llega al final del cuerpo de la
funci6n. En este instante, el control es devuelto al punta de llamada para
continuar la ejecuci6n del programa.
La declaraci6n de una funci6n, denominada tambien funcion prototipo,
permite conocer el nombre, el tipo del resultado, los tipos de los panime-
tros formales y opcionalmente sus nombres. No define el cuerpo de la fun-
cion.Esta informaci6n permite al compilador chequear los tipos de los pa-
nimetros actuales por cada llamada a la funci6n. Una funci6n no puede
ser Hamada si previamente no esta definida 0 declarada. A esta regIa hay
una excepci6n que veremos a continuaci6n.
static double escribir(double x, int y) / *funci6n escribir */
[
return x +y *1.3;
J
main( ) / *funci6n principal */
[
double r, a = 3.14;
int b = 5;
r = escribir(a, b); / * llamada a la funci6n escribir */
printf("%g  n': r);
J
Observar que la definici6n de la funci6n es anterior a la llamada. Si
estono sucede asi, entonces es necesario declarar la funci6n antes de que
Una funcion prototipo tiene la misma sintaxis que la definicion deuna
funcion, excepto que esta termina con un punta y coma.
..A~~.)lamada. La dec1aracion de una fundon puede ocurrir a nivel interno
':(j" a nivel externo.
'1,::.-..........
Ajxp;tRl,~,.",. ",j.ftti-.,.
,,:.'/'#f#clude <stdio.h>
. .,.' _...•~.
',:." !l{
static double escribir(double x, int y); / *funci6n prototipo */
main() / *funci6n principal */
(
double r, a = 3.14;
int b = 5;
r = escribir(a, b); / * llamada a la funci6n escribir */
printj(H%g  n': r);
}
static double escribir(double x, int y) / *funci6n escribir */
{
return x +y *1.3;
}
Una fundon prototipo recibe tambien el nombre de declaracion fOf·
ward; esto es, una dec1aracion que precede a la definicion de la fundon.
Si una Hamada a una fundon precede a su definicion, y no existeuna
declaracion forward de la misma, implidtamente esta se construye con tipo
de resultado into
main( )
(
int r, b = 5;
r = escribir(b); / * llamada a la funci6n */
/ * Este programa busca un ntimero en una !ista
* e injorma de su posicion
*/
#include <stdio.h>
# include <std!ib.h >
# include <ctype.h >
#dejine N 40
const int NO = 0;
main( )
(
float !ista[N];
float numero,o
int i = 0;
int encontrado;
/ * !ista de mlmeros */
/ * valor a buscar */
/ * ntimero de valores lefdos */
/ * 0 = no encontrado
* otro valor = posicion del n° en la !ista
*/
system ("cls ");
printj(Hlntroducir !ista de ntimeros.  n");
printj("Fina!izar con A Z.  n  n");
while (scanj("%f': &!ista[i+ +J) != EOF && i < N)
clearerr(stdin); / * desactivar el indicador EOF */
/ * Busqueda de un numero cualquiera en la !ista */
do
(
system("cls");
printj("Indique el ntimero que desea buscar ");
scanj("%f': &numero);
encontrado = BusquedaSecuencial(!ista, i, numero);
if (!encontrado)
printj("  nEste n° no estci en la !ista  n ");
else
printj(" nEste nO estci en la posicion %d n': encontrado);
printft'  n  n;, Desea continuar ? sin "),o
do
resp = getch( ),o
while (tolower(resp)!= 's' && tolower(resp) != 'n'),o
l
while (resp = = 's'),o
l
1**************************************************************
Busqueda Secuencial
**************************************************************1
int BusquedaSecuencial(f1oat lista[N], int i, float numero)
[
int encontrado = NO, k 0;
while (!encontrado && k < i)
{
if (lista[k+ +] = = numero)
encontrado = k,o
l
return encontrado,o
l
Observar que la entrada de datos se realiza mediante la repetici6n de
la ejecuci6n de una sentencia scanf(). Esta entrada finaliza cuando se pul-
sen las teclas etr) +Z (F6), 10 que hace que se active el indicador EOF de
fin de fichera. Este indicador queda activado mientras no se desactive ex-
plicitamente, 10 que da lugar a que las siguientes sentencias scanf( ) no sean
ejecutadas. Pues bien, para desactivar el indicador EOF es necesario eje-
cutar la funci6n clearerror(stdin).
1. Almacene en una array, el numera de matricula, apellidos, nom-
bre y direcci6n de cada uno de 10s alumnos de un determinado
curso.
2. Busque la ficha carrespandiente a un alumna, par su numero de
matricula.
# include <stdio.h>
# include < stdlib.h >
# include < ctype.h >
# include <string.h>
int leer(int);
void buscar(char *, int, int);
char resp;
main( )
{
char m[30};
int opcion, n = 0;
while (1) / * bucle infinito; se sale con break */
{
do
(
system ("cls ");
printj("  n  tl. Entrada de datos de alumnos  n ");
printjt'  n  t2. Bzisqueda por nro. de matrfcula  n");
printj("  n  t3. Bzisqueda por apellidos  n ");
printj("  n  t4. Fin n ");
printj("  n  nTeclee l~ opcion deseada ");
scanf("%d': &opcion);
J
while (opcion < 1 II opcion > 4);
fflush(stdin); / * limpiar el buffer de la errirada estdndar */
if (opcion 1= 4)
{
switch (opcion)
{
case 1: I * entrada de datos de alumnos *1
resp = 's';
while (tolower(resp) = = 's')
[
leer(n+ +);
printj(ff  n  nj, Mas datos a introducir ? sin ");
resp = getche( );
}
break;
case 2: 1* btisqueda por ntimero de matrfcula *1
systemt 'cis' ');
printf(ffNtimero de matrfcula "); gets(m);
buscar(m, n, opcion);
break;
case 3: 1* Btisqueda por apellidos *1
system(ffcls");
printj(ffApellidos.......... "); gets(m);
buscar(m, n, opcion);
break;
}
}
else
break;
1**************************************************************
Funci6n para leer los datos correspondientes a un alumno
**************************************************************1
#deJineN 20 1* ntimero maximo de alumnos *1
struct Jicha
[
char matricula[10];
char apellidos[30];
char nombre[20];
char direccion[30];
} lista[N];
int leer(int n)
{
do
(
system("c!s ");
printf("Alumno mimero %d n  n': n + l),~
printj("Numero de matrfcula "); gets(lista[n};matricula);
printj("Apellidos................ "); gets(lista[h};apellidos);
printf("Nombre........... . "); gets(lista[hf.nombre);
printf("Direccion "); gets(lista[n}.direccion);
printj("  n  nz Datos correctos ? sin ");
resp = getche( );
l
while (tolower(resp) /= 's');
J
1**************************************************************
Funcion para buscar si existe 0 no un dato
**************************************************************/
const int NO = 0;
const int S1 = 1;
void buscar(char x[30}, int alumnos, int opcion/
(
int existe = NO, i = 0;
switch (opcion,)
{
case 2: 1* busqueda por numero de matrfcula *1
while (/existe && i < alumnos)
if (strcmp(lista[i + +}.matricula, x) = = 0)
existe = S1;
break;
.:ase 3: 1* Busqueda por apellidos *1
while (/existe && i < alumnos)
if (strcmp(lista[i+ +j.apellidos, x) - - 0)
existe = S1;
break;
if (existe)
printj("  n%s  n%s %s  n%s  n': lista[i-1].matricula,
lista[i-l].apellidos,
lista[i-1].nombre,
lista[i-1].direccion);
clSe
printjt'  n%s no existe': x);
printj("  n  nPulse una tecla para continuar ");
resp = getch( );
)
Es necesario tener en cuenta que, despues de ejecutarse la sentencia
scanf("%d': &opcion), en el buffer de la entrada estandar stdin queda el
caracter ' n' (separador para scanf( )). Como es un caracter valida para
una funcion como gets( ), es necesario limpiar este buffer, antes de que
se ejecute una funcion como esta. Esta operacion se realiza mediante la
funcion jjlush(stdin).
Realizar un programa que lea una fecha comprendida entre los afios
1980y 2100, verifique si es valida y la imprima
# include <stdio.h >
# include <stdlib.h>
main( )
{
unsigned int dia, mes, anno, s;
do
(
system("cls ");
s=l; I*s
1* s =
1, indica jecha valida *1
0, indica jecha no valida *1
printj(Hlntroducir la jeeha:  n");
printj(H. dfa: "); seanj(H%u': &dia);
printj(H mes: "); seanj(H%u': &mes);
printj(H ano: "); seanj(H%u': &anno);
s = jeeha(dia, mes, anno);
--I
while (s = = 0);
}
1**************************************************************
Verifiear e imprimir jeeha
**************************************************************(
int jeeha(int dd, int mm, int aa)
(
unsigned int diasmes;
int c1, e2;
1* Caleular los dfas del mes *1
witch (mm)
(
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
diasmes = 31;
break;
case 4: case 6: case 9: case 11:
diasmes = 30;
break;
case 2: 1* eomprobar si el ano es bisiesto *1
if ((aa % 4 = = 0) && (aa % 100 1= 0) II (aa % 400 0))
diasmes = 29;
else
diasmes = 28;
break;
default:
putehart  7'); 1* earaeter bell *1
return (0); 1* jeeha no valida *1
}
c1 = dd > = 1 && dd < = diasmes;
e2 = aa > = 1980 && aa < = 2100;
if (c1 && e2)
(
printj(H ndfa-mes-ano: %U-%U-%IJ  n': dd, mm, aa);
return (1);
J
else
[
putchar(  7');
return (0);
J
J
/ * caracter bell */
/ *fecha no valida */
Hay dos formas de pasar los parametros actuales a sus correspondientes
parametros formales, cuando se efectua la llamada a una funci6n:
1. Por valor.
2. Por referencia.
Pasar parametros por valor, significa copiar los parametros actuales
en sus correspondientes parametros formales, operaci6n que se hace auto-
m<iticamentecuando se llama a una funci6n, con 10 cual no se modifican
los parametros actuales.
Pasar parametros por referencia, significa que 10 transferido no son
losvalores, sino las direcciones de las variables que conti enen esos valores,
can 10 cual los parametros actuales pueden verse modificados.
Cuando se llama a una funci6n, los argumentos especificados en la
Hamada son pasados por valor; excepto los arrays que se pasan por refe-
rencia, ya el nombre del array es un puntero a dicho array.
Utilizando la forma de pasar parametros por valor, pueden ser trans-
feridas constantes, variables y expresiones; y utilizando la forma de pasar
parametros por referencia, solamente se permite transferir las direcciones
de variables de cualquier tipo, arrays y funciones.
Para pasar una variable por referencia, se pasa la direcci6n del para-
metro actual a su correspondiente parametro formal, el cual tiene que ser
un puntero. Para ello, utilizar el operador & antes del nombre de la varia-
ble. Para pasar la direcci6n de un array 0 de una funci6n, no es necesario
esteoperador antes del nombre del array 0 del nombre de la funci6n.
main( )
(
int v = 5, suma;
sumar(4, v, v*2-1, &suma); / * llamada a fa funci6n */
printf(H%d n': suma);
l
int sumar(int a, int b, int c, int *-5)
(
b += 2;
*-5=a+b+c;
La Hamada a la funci6n sumar( ), pasa a esta funci6n los panimetros
4, v y v*2-1 por valor y el panimetro sum a por referencia.
Cualquier cambio que sufra el contenido del panimetro formal s, su-
cede tambien en su correspondiente panimetro actual suma. En cambia,
la variable v, no se ve modificada, a pesar de haber variado b, ya que ha
sido pasada por valor.
Para pasar todos los elementos de un array a una funci6n, se pone enla
lista de panimetros actuales el nombre del array, que es la direcci6n de co-
mienzo del array; y en la lista de panimetros formales, el nombre del array
seguido de sus dimensiones. De estas, como ya dijimos, se puede omitir
la primera, pero no los corchetes que la contienen.
#include <-sttiio.h>
#include <stdlib.h >
#dejine FILAS 12
#dejine COLS 4
void ElementosNulos(f1oat !][COLS], const int, const int, int *);
main( )
I
int cont;
system((cls ");
ElementosNulos(datos, FILAS, COLS, &cont);
printj((Hay %d elementos nulos  n': cont);
l
void ElementosNulos(f1oat mat! ][COLS], const int ji/as,
const int cols, int *conta)
1* Da como resultado el ntimero de.elementos igual
* a cero, en un array de dos dimensiones.
*/

int 1, c;
~onta = 0; mat[1l][3] = 100;
for if = 0;j < ji/as; j + +)
for (c = 0; c < cols; c+ +)
if (matff][c] = = 0) + + *conta;
El panimetro formal mat! ][COLS], puede especificarse tambien de
la forma: mat!FILAS][COLSj.
La variable cont se pasa por referencia. Cualquier cambio que sufra
~onta, 10 sufflra1ambien su correspondiente panimetro actual cont. Igual-
mente sucedeni con los elementos del array.
Otra forma de pasar un array es utilizando la notaci6n de punteros.
Aplicando esta notaci6n al ejemplo anterior, obtendriamos:
void ElementosNulos(f1oat **mat, const int Ji/as,
const int cols, int *conta)
Realizar un programa que lea lineas de texto y nos de como resultado
la linea mas larga. Cada linea finalizara con un retorno de carro y el texto
con EOP.
# include <stdio.h >
# include <stdlib.h>
void leer_linea(char linea! ], int *long_linea);
void copiar(char linea! ], char linea_maxI ], int long_max);
main( )
{
char linea[CARS-LINEAJ, linea_max[CARS-LINEAJ;
iot long_linea = 0, long-"lax = 0;
systemt<c/s");
printf(Hlntroducir lfneas de texto. Finalizar con AZ.  n  n");
while ((linea[OJ = getchar( )) != EOF)
{
long_linea = 1;
leer_linea(line~ &long_lineat
if (long_linea > long-"lax)
{
long_max = long_linea;
copiar(linea, linea_max, long_max);
}
}
if (long_max > 0)
{
printf(H nLfnea de texto mds larga: n");
printf(H n%s  n': linea_max);
}
}
1**************************************************************
Funci6n leer /(nea
**************************************************************/
void leer_linea(char linear J, iot *long_linea)
{
while ((lineablong_lineaJ = getchar( )) != <  n' &&
*long_linea < CARS-LINEA-1)
+ + *long_linea;
/ * la cadena se jinaliza con el cardcter nulo t  0').
* convenio utilizado por C
*/
linear*long_lineaJ = <  0';
J
/**************************************************************
Funcion copiar
**************************************************************/
/ * Guardar en linea_max la lfnea mas grande en curso */
void copiar(char linea! ], char linea_maxI ], int long_max)
[
for (i =0; i < = long_max; i+ +)
linea-"lax!i] = linea!i];
La fundon leer_linea(), lee una linea de texto y calcula su longitud.
Si resulta que esta linea es la mas larga de las leidas hasta ahora, la fundon
copiar( ) la guarda en el array linea_max, para al final escribirla.
Un puntero igual que otros tipos de variables puede ser pasado por valor
o por referenda.
# include < stdio.h >
# include <string.h >
struct p
[
char c!20];
int n;
};
typedef struct p pareja;
main( )
[
pareja *0 = NULL, *b = NULL;
juncion.-X(a, &b);
printj("pareja apuntada por a: %-20s %5d n': a->c, a->n);
printf("pareja apuntada por b: %-20s %5d  n': b- >c, b- >n);
)
void juncion.-X(pareja *p, pareja **lJ)
[
P = (pareja *)malloc(sizeof(pareja));
strcpy(p- >c, "cadena a"), p- >n = 1;
*q = (pareja *)malloc(sizeof(pareja));
strcpy((*lJ)->c, "cadena b"), (*lJ)->n = 2;
)
pareja apuntada por a: (null)
pareja apuntada por b: cadena b
26956
2
En el ejemplo anterior, tratamos de asignar un valor alas estructuras
apuntadas por a y por b, utilizando una funci6n. Esta fund6n tiene dos
parametros formales, correspondientes a los parametros actuales a y b, los
cuales son: p puntero a un objeto de tipo pareja y q que es un puntero a
un puntero a un objeto de tipo pareja. En base a 10 expuesto, a es pasado
por valor y b es pasado por referenda.
La funci6njuncion.-X(), primeramente asigna memoria para un ob-
jeto de tipo pareja y 10 deja apuntado por p. Este objeto no es visible en
main(), ya que en la llamada a la fund6n 10 que pasamos a p, fue el valor
de a. Ahora p, ha tornado un nuevo valor que no es reflejado en a.
A continuad6n se asigna memoria para otro objeto de tipo pareja,
el cual queda apuntado por q. Este objeto es visible en main( ), ya que q
referenda a b, por 10 que todo cambio efectuado sobre *q, se vera tambien
reflejado sobre b.
Otro detalle que cabe resaltar, es la utilizaci6n de la funci6n strepy( )
para copiar una cadena en otra. Seguramente algunos intentarian resolver
esta cuesti6n utilizando, por ejemplo, la sentencia:
char a[20], b[20] = "abed";
a = b; / * error */
El error que se obtiene es debido a que a es una constante, por 10 que
su valor no es modificable. En cambio si realizamos las declaraciones:
char ~, *b = "abed";
I = b; / * eorreeto */
el resultado es correcto, ya que a es una variable. En este caso estamos asig-
nando e1valor de la variable puntero b, a: la variable puntero a. Ahara a
y b apuntan a la cadena "abed': La funci6n strepy( ) copia contenidos,
no direcciones.
Cuando se ejecuta un programa C, la primera funci6n que se llama
para su ejecuci6n es main( ). En general, el formate para esta funci6n es:
En muchas ocasiones cuando invocamos un programa desde e1sistema ope-
rativo, necesitamos escribir uno 0 mas argumentos a continuaci6n del nom·
bre del programa, separados por blancos.
En este ejemplo, progQl es el program a que ejecutamos y -n es el argumen-
to a procesar inmediatamente, bajo este programa. La cadena "-n" recibe
el nombre de argumento en linea de 6rdenes.
argc es un entero que indica el numero de argumentos en la linea de
ordenes.
argv es un array de punteros a cadenas de caracteres. Cada elemento
del array apunta a un argumento, de manera que: argv[O]contie-
ne el nombre del programa, argv[1]el primer argumento de la li-
nea de ordenes, argv[2] el segundo argumento, etc.
Los argumentos de main( ) se han denominado arge y argv por conve-
nio.Esto quiere decir que podriamos utilizar otros identificadores para nom-
brarlos.
El siguiente ejemplo muestra como acceder al contenido de estos pa-
nimetros desde un programa. El programa que se presenta a continuacion,
simplemente escribe los argumentos pasados desde la linea de ordenes a
la funcion main( ).
main(int arge, char *argv[J)
[
int i;
if (arge < 2) / * ehequear el mlmero de argumentos */
printft'No hay argumentos para %s  n': *argv);
else
[
printf("Nombre del programa: %s  n  n': *argv);
argv+ +, arge--; / * eliminar el argv[O] */
printj("Nl1mero de argumentos pasados: %d  n  n': arge);
for (i = 1; i < = arge; i+ +)
printj(" argumento %d: %s  n': i, ~rgv+ +);
Orden de ejecucion:
C:  >prog01 uno dos tres
argumento 1: uno
argumento 2: dos
argumento 3: tres
Por ser argv un array de punteros a cadenas, existen varias formasde
acceder al contenido de sus elementos. Estas son:
EI numero de parametros de una funcion puede ser variable. La notaci'
empleada. para especificar esta caracteristica es la elipsis; tres puntos (...
Por ejemplo:
Este ejemplo declara una funcion que no retorna un valor, y que p
de tomar un numero variable de argumentos de tipo no especificado,
Si algunos argumentos, pero no todos, son conocidos, estos deben
listados primero. En este caso, el numero de argumentos especificados
la Hamada, debe ser como minima igual al numero de parametros for
les que aparecen en la definicion de la funcion.
Este ejemplo declara una funcion llamada buscar, que acepta al me-
nos un argumento, ptr, declarado como un puntero a un valor de tipo char.
El valor retornado por la funcion es un puntero a un valor de tipo char.
Si la funcion espera un numero de argumentos variable y de tipos no
conocidos, tendremos que utilizar funciones con un mimero de argumen-
tos variable.
Todos los argumentos especificados en la llamada son colocados en
la pila (stack). El recuperar de la pila estos argumentos es responsabilidad
del programador. Para ello, y con el fin de tener un punta de referencia,
se debe especificar al menos un argumento; este no puede ser de tipo void
ni de clase de almacenamiento register.
Cuando un argumento no es declarado, el com pi lad or no tiene la in-
formacion necesaria para ejecutar el chequeo y conversion sobre dicho pa-
nimetro, dejando esta problematica al programador.
Con el fin de salvar los problemas anteriores y hacer mas sencilla la
manipulacion de funciones con un numero variable de argumentos, hay
construidas un conjunto de macros estandar, las cuales se localizan en el
fichero stdarg.h. Estas macros son las siguientes:
Se utiliza para declarar la variable ap (puntero a un char), que utiliza-
remos para direccionar sucesivamente, los argumentos colocados en la pBa
despues de la llamada a la funcion.
Coloca en la variable ap la direccion del primer argumento no nom-
brado. Esta direccion se calcula a partir de v, que es el nombre del ultimo
argumento especificado.
Calcula la direcci6n del siguiente argumento de tipo t no nombrado,
y da como resultado el valor (no la direcci6n, debido a [-1]) del argumento
colocado en la direcci6n ap anterior (sub in dice -1).
Inicializa la variable ap al valor NULL. Cuando necesitamos recupe-
rar mas de una vez los argumentos, es necesario Hamar previamente a esta
funci6n.
Supongamos que queremos escribir una funci6n error( ) que toma un
numero entero de argumentos, los cuales indican la gravedad del error por
un numero arbitrario de cadenas de caracteres. La idea es formar el men-
saje de error a partir de las palabras pasadas como argumentos en linea
de 6rdenes.
# include <stdio.h>
# include <stdlib.h >
# include <stdarg.h >
void error(int, ...);
main(int argc, char *l1rgv[ J)
(
switch (argc)
(
case 1:
error(O, argv[O], 0);
break;
case 2:
error(O, argv[O], argv[1], 0);
break;
default:
if?afargc-l, aro:s, 10);
void error(int n, ...)
(
va_list p;
va-start(p, n);
/ *p es una variable de tipo va_list */
/ *p = direcci6n primer argumento */
while (1)
(
char *fJ = va_arg(p, char *);
if (q)
printf("%s ': q);
else
break;
putchart  n');
if (n) exit(n);
Primeramente definimos la variable pyla inicializamos llamando a
la macro va-start( ). Esta macro toma como argumentos la variable p y
el ultimo argumento formal declarado en la funcion. La macro va_arg( )
es utilizada para coger los argumentos no nombrados, en orden. En la lla-
mada, el programador debe especificar el tipo. Antes de retornar desde una
funcion que ha ejecutado la macro va-start( ), debe ejecutarse la macro
va_end( ). La razon es que va-start( ) puede modificar el stack de tal
forma que impida un retorno satisfactorio. La macro va_end( ) deshace
tales modificaciones.
Sedice que una fundon es recursiva, si se llama a sl misma. El compilador
C permite cualquier numero de llamadas recursivas a una funcion. Cad a
vezque la fundon es llamada, los panimetros formales y las variables auto
y register son inicializadas. Notar que las variables static solamente son ini-
cializadas una vez, en la primera Hamada.
La funci6n factorial, cuyo programa se presenta a continuaci6n, es
recursiva.
# include <stdio.h >
# include <stdlib.h>
long jactorial(int n);
main( )
{
int numero;
long jac;
system(' 'cls");
printj('';, Ntimero ? "); scanj(H%d'; &numero);
jac = factorial(numero);
printj(H  nEI jactorial de %2d es %ld  n'; numero, jac);
if (n = = 0)
return 1;
else
return n 4actorial(n-1);
long jactorial(int n)
{
En el esquema siguiente se ve el proceso seguido por la funci6n, du-
rante su ejecuci6n para n =4.
factorial( 4)
4 * factorial(3)
3 * factorial(2)
2 * factorial(1)
1 * factorial(O)
factorial(O)
24
4 * 6
3 * 2
2 * 1
1 * 1
I
Cada Hamada a la funci6n factorial aumenta en una unidad el nivel
de recursi6n. Cuando se Hega a n = 0, se obtiene como resultado el valor
1y se inicia la vuelta hacia el punta de partida, reduciendo el nivel de re-
cursi6n en una unidad cada vez.
Los algoritmos recursivos son particularmente apropiados cuando el
problema a resolver 0 10sdatos a tratar se definen en forma recursiva. Sin
embargo, el uso de la recursi6n debe evitarse cuando haya una soluci6n
obvia por iteraci6n.
En aplicaciones pnicticas es imperativo demostrar que el nivel maxi-
mo de recursi6n es, no s610 finite, sino realmente pequeno. La raz6n es
que, por cada ejecuci6n recursiva de la funci6n, se necesita cierta cantidad
de memoria para almacenar las variables locales y el estado en cursa del
proceso de calculo con el fin de recuperar dichos datos cuando se acabe
una ejecuci6n y haya que reanudar la anterior.
Como hemos indicado anteriormente, elHamar a una funci6n recursiva-
mente, consume mucho espacio de pila debido a que par cada Hamada las
variables que intervienen en la funci6n son salvadas en la pila para poste-
riormente poder iniciar la vuelta. Esto indica que puede ser necesario ajus-
tar el tamano de la pila mediante la opci6n IF de la orden CL.
Tenemosdos listas de palabras clasificadas en orden ascendente. Se pide
realizarun programa que fusione ambas listas, en otra tambien clasificada.
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define dimA 12
# define dimN 8
# define dimF 20
main( )
[
char listaFinal[dimF][60];
int ind, r;
static char listaActual[dimA][60] =
[ <~na': "Carmen': "David':
"Francisco': "Javier': "Jesus':
"Jose': "Josefina': "Luis':
"Marfa': "Patricia': "Sonia"};
static char listaNueva[dimN][60] =
[ <~gustfn': "Belen':
"Daniel': "Fernando': "Manuel':
"Pedro': "Rosa': "Susana"};
system("cls ");
if (r)
[
for (ind = 0; ind < 20; ind + +)
printj("%s  n': listaFinal[ind]);
}
else
int jusionar(char listaA[ ][60J, char listaN[ ][60J,
char listaF[ ][60])
(
int ind, indA = 0, indN = 0, indF = 0;
if (dimF < dimA + dimN)
retu rn (0);
while (indA < dimA && indN < dimN)
if (strcmp(listaA[indAJ, listaN[indN]) < 0)
strcpy(listaF[indF + +J, listaA[indA + +]);
else
strcpy(listaF[indF + +J, listaN[indN + +]);
/ * Los dos lazos siguientes son para prever el caso de que,
* 16gicamente una lista jinalizard antes que otra.
*/
for (ind = indA; ind < dimA; ind + +)
strcpy(listaF[indF + +J, listaA[ind]);
for (ind = indN; ind < dimN; ind + +)
strcpy(listaF[indF + +J, listaN[ind]);
return (1);
)
Realizar un programa que cuente el numero de veces que aparece cada
una de las letras (a - z) en un texto introducido por tec1ado. El texto queda-
ni almacenado en un array.
/ * Contar el numero de veces que aparece cada letra
* en una cadena de caracteres.
*/
# include <stdio.h>
# include <stdlib.h >
# include <ctype.h >
# include <string.h>
main( ) / * Funci6n Principal */
{
static iut contaf'z'- 'a' +Ij;
/ * Un array estdtico es inicializado automdticamente a ceros */
char texto[IOOOj,car;
iut i = 0;
system("cls");
/********************* l?ntrada de datos *********************/
printj("Introducir texto. Finalizar con AZ.  n  n");
while ((texto[i + +j = getchar( )) != l?OF);
texto[i] = ' 0';
for (car = 'a'; car < = 'z'; car+ +)
printj(" %c'~ car);
printj("  n ---------------------------------------------------------------  n  n");
for (car = 'a'; car < = 'z'; car+ +)
printj("%2d'~ conta[car- 'a']);
putchar('  n ');
l
1**************************************************************
Contar Letras
**************************************************************/
void ContarLetras(char text[ J, int conta[ J)
{
int i;
for (i = 0; i < strlen(text); i+ +)
if (tolower(text[i]) > = ca' && tolower(text[i]) < = Cz')
conta[text[iJ - ca']+ +;
El calendario Gregoriano actual obedece a la reforma del calendario julia-
no que orden6 el papa Gregorio XIII en 1582. Se decidi6, despues de algu-
nas modificaciones, que en 10 sucesivo fuesen bisiestos todos los alios mul-
tiplos de cuatro, pero que de los alios seculares (Ios acabados en dos ceros)
s6lo fuesen bisiestos aquellos que fuesen multiplos de cuatrocientos. En
base a estos conceptos, construir un programa para que dada una fecha
(dia, mes y alio), nos devuelva como resultado el correspondiente dia de
la semana.
La descomposici6n en subproblemas que se ha hecho en la realizaci6n
de este ejercicio es la siguiente:
Dia de la
semana
Entrada
datos
Validar
datos
Inicializar
variables
Determinar
dia de la
semana
**************************************************************/
/* Dada una jecha (dia, mes, ana)
* indicar el dfa correspondiente de la semana.
*/
# include <stdio.h >
# include <stdlib.h >
void LeerFecha (jot ~ia, iot *mes, iot ~nno);
void EscribirFecha ( iot dd, iot mm, iot aa);
void EntradaDatos (jot ~ia, iot *mes, iot *anno);
iot DatosValidos (jot dia, iot mes, iot anno);
iot AnnoBisiesto (jot anno);
iot DiaSemana (jot dia, iot mes, iot anno);
LeerFecha(&dia, &mes, &anno);
EscribirFecha(dia, mes, anno);
J
main() / * Funci6n Principal */
[
iot dia, mes, anno;
void LeerFecha (jot ~ia, iot *mes, iot ~nno)
[
iot datos_validos;
EntradaDatos(dia, mes, anno);
datos_ validos = Datos Validos(*dia, *mes, *anno);
J
while (!datos_validos);
J
void EntradaDatos(int ..dia, int *mes, int ~nno)
[
systemt <cls''),o
printj(ffDfa (1 - 31) "),o scanj(ff%d': dial;
printj(ffMes (1 - 12) "),o scanj(ff%d': mes),o
printf(ffAno (1582 -- » "),o scanj(ff%d': anno),o
l
int DatosValidos(int dia, int mes, int annal
[
int r, annoB, mesB, diaB,o
annoB = (anno > = 1582);
mesB = (mes > = 1) && (mes < = 12);
switch (mes)
[
case 2:
if (r = AnnoBisiesto(anno))
diaB = (dia > = 1) && (dia < = 29);
else
diaB = (dia > = 1) && (dia < = 28);
break;
case 4: case 6: case 9: case 11:
diaB = (dia > = 1) && (dia < = 30);
break;
default:
diaB = (dia > = 1) && (dia < = 31);
l
if (!(diaB && mesB && annoB))
[
printj(ff  nDATOS NO VAL/DOS  n  n"),o
printj(ffPu/se una tecla para continuar "),o
r = getch( ),o
return (0);
l
else
return (1);
int AnnoBisiesto(int annal
[
int verdad = 1, fa/so = 0;
HSdbado': HDomingo': 'Tunes':
HMartes': HMiercoles': HJueves':
HViernes" };
HEnero': HFebrero': HMarzo':
HAbril': "Mayo': HJunio': HJulio':
'~gosto': HSeptiembre': HOctubre':
HNoviembre': HDiciembre"};
if ((anno % 4 = = 0) && (anno % 100/= 0) II (anno % 400 = = 0))
return (verdad);
else
return (falso);
void EscribirFecha(int dd, int mm, int aa)
{
int d;
static char dia[7][1O]
d = DiaSemana(dd, mm, aa);
printf("  n%s %d de %s de %d n':dia[d], dd, mes[mm-1], aa);
1
int DiaSemana(int dia, int mes, int anno)
{
if (mes < = 2)
{
mes = mes + 12;
anno = anno - 1;
}
return ((dia+2*mes+3 4mes+ 1)/5+anno+anno/4-anno/100+
anno/400+2) % 7);
Igual que sucedia con 10s arrays, el nombre de una funcion representa la
direccion de comienzo de la funcion; por 10tanto, puede ser utilizado para
pasarl0 a funciones, colocarlo en arrays, etc.
p-.-identif identifica a una variable tipo puntero. Esta variable recibini un
puntero a una funci6n, dado por el propio nombre de la funci6n.
En el siguiente ejemplo, se define un puntero p a una funci6n. A con-
tinuaci6n, se asigna a p la direcci6n de la funci6n escribir y se llama a la
funci6n mediante la sentencia: (*p)(5);.
# include <stdio.h >
void escribir(int);
main( )
{
void (*p)(int);
p = escribir;
(*p)(5);
/ *p = direcci6n de fa funci6n */
/ * llamada a fa funci6n */
void escribir(int a)
{
printj(f'%d  n': a);
J
EI nombre de una funci6n representa la direcci6n de comienzo de la
misma.
EI siguiente ejemplo clarifica esta caracteristica de C. EI programa que
semuestra a continuaci6n, busca y escribe el valor menor de una lista de
datos numerica 0 de una lista de datos alfanumerica. Si en la linea de 6r-
denesse pasa un argumento "n", se interpreta la lista como numerica; en
10sdemas casos se interpreta como alfanumerica.
EI programa consta fundamentalmente de una funci6n fmenor( ) que
buscaen una lista de datos, el menor, independientemente de las operacio-
nesde comparaci6n (numeric a 0 alfanumerica). Para ello Ie pasamos fun-
ciones diferentes para comparar. Para hacerlo numericamente, se utiliza la
funcion compnu( ) y para comparar alfanumericamente, se utiliza la fun-
cion compal( ).
# include <stdio.h>
# include <std!ib.h>
# include <string.h>
# include <math.h >
# define FMAX 100
main(int argc, char ~rgv[ ])
[
char *pcadena[FMAX];
char dato[81];
char *p;
char *compnu(char *, char *);
char *compal(char *, char *);
char 4menor(int, char **, char *(*)(char *, char *));
int c = 0, longitud;
/ * array de punteros a los datos */
/* dato */
/ * Leer !ista de datos numericos 0 a/fanumericos */
printjt'Entrar datos y fina!izar con Enter  n  n");
while (c < FMAX)
[
printj(HDato %d: ': c + 1);
if ((longitud = strlen(gets(dato))) 0)
break;
else
[
p = (char *)malloc(longitud +1);
if (p = = NULL)
[
printj(Hlnsuficiente espacio de memoria  n");
exit(l);
}
else
[
strcpy(p, dato);
pcadena[c+ +] = p;
l
l
l
/* argv[l] 1= "n" -> busqueda en una !ista alfanumerica,
* argv[l] = "n" -> busqueda en una !ista numerica
*/
if (argc > 1 && argv[l][O] = = 'n')
p = jmenor(c, pcadena, compnu);
else
p = jmenor(c, pcadena, compal);
printf("  n  nEl elemento menor de la !ista es: %s  n': p);
l
char 4menor(int c, char *pcadena[ ],
char *(*comparar)(char *, char *))
/ * Buscar el dato menor de una lista */
[
char *menor;
menor = *pcadena; / * menor = primer dato */
while ( --c > 0)
/ * comparar men or con el siguiente dato */
menor = (*comparar)(menor, * + +pcadena);
return (menor);
l
char *Compnu(char *px, char *py)
/ * Camparar dos datos numericamente */
I
if (ataf(px) > atof(py))
return (py);
else
return (px);
char *compal(char *px, char *py)
/ * Camparar dos datos alfanumericamente */
I
if (strcmp(px, py) > 0)
return (py);
else
return (px);
p = fmenor(c, pcadena, compnu);
p = jmenor(c, pcadena, compa/);
char 4menor(int c, char *pcadena[ },
char *(*comparar)(char *, char *))
Los panimetros actuales compnu y compa/ son punteros alas funcio-
nes del mismo nombre. EI panimetro formal correspondiente, (~omparar)()
dice que comparar es un puntero a una funcion que devuelve un puntero
a una cadena de caracteres. Cuando el panimetro pasado es compnu, se
ejecuta la funcion compnu( ) y cuando el panimetro pasado es compal,
se ejecuta la funcion compa/( ).
En este capitulo hemos estudiado como el usuario puede definir sus pro-
pias funciones. No obstante C dispone en sus librerias de mas de 400 fun-
ciones; algunas de ellas ya las hemos visto, como las funciones para entra-
da/salida, las funciones para manipular cadenas de caracteres etc., y otras
las iremos viendo en este y en sucesivos capitulos.
Las declaraciones para las funciones matematicas que a continuacion se
describen, estan en el fichero a incluir math.h. Quiere esto decir, que cuan-
do se utilice una funcion matemcitica en un programa, debe especificarse
la directriz:
Los argumentos para estas funciones son de tipo double y el resultado
devuelto es tambien de tipo double. Por ello, en muchos casos utilizare-
mos la construcci6n cast para convertir explicitamente los argumentos al
tipo deseado.
La expresi6n (double) hace que valor sea convertido a tipo double an-
tes de ser utilizado.
Podemos clasificar las funciones matemcHicas en las siguientes cate-
gorias:
Esta funci6n da como resultado el arco, en el rango 0 a 1f, cuyo coseno
es x. EI valor de x debe estar entre -1 y 1; de 10 contrario se obtiene un
error DOMAIN (argumento fuera del dominio de la funci6n).
Esta funci6n da como resultado el arco, en el rango -1f12 a 1f12, cuyo seno
esx. EI valor de x debe estar entre -I y 1, si no se obtiene un error DOMAIN
(argumento fuera del dominie de la funci6n).
Esta fund6n da eomo resultado el areo, en el rango -7r12 a ni2, euya tan-
gente es x.
Esta funei6n da eomo resultado el areo, en el rango -7r a 7r, euya tangente
es y/x. Si ambos argumentos son 0, se obtiene un error DOMAIN (argu-
mento fuera del dominio de la fund6n).
# include <math.h >
main( )
{
double valor -1;
do
{
printf(H%lf %If n': acos(valor), atan2(valor, 1.0));
valor + = 0.1;
}
while (valor < = 1.0);
}
Esta funci6n da como resultado la tangente de x (x en radianes).
double tan(double x);
Esta fund6n da como resultado el coseno hiperb6lico de x (x en radianes).
double cosh(double x);
Esta funci6n da como resultado el seno hiperb6lico de x (x en radianes).
double sinh(double x);
Esta funci6n da como resultado la tangente hiperb6lica de x (x en radianes).
double tanh(double x);
Esta fund6n da como resultado el valor de eX (e
double exp(double x);
Esta fund6n da como resultado el logaritmo natural de x.
double log(double x);
Esta funci6n da como resultado un valor double, que representa el entero
mas pequeno que es mayor 0 igual que x.
double x = 2.8, y = -2.8;
printj(<t%g %g  n': cei/(x), cei/(y));
Esta funci6n da como resultado el valor absoluto x. El argumento x, es
un valor real en doble precisi6n. Igualmente, abs y labs dan el valor abso-
luto de un int y un long respectivamente.
Esta funci6n da como resultado un valor double, que representa el entero
mas grande que es menor 0 igual que x.
double x = 2.8, y = -2.8;
printj(<t%g %g  n': jloor(x), jloor(y));
Esta funcion da como resultado xY• Si x es 0 e y negativo; 0 si x e y son
0; 0 si xes negativo e y no es entero, se obtiene un error DOMAIN (argu-
mento fuera del dominio de la funcion). Si xY da un resultado superior al
valor limite para el tipo double, el resultado es este valor limite
(1.7976ge +308).
double x = 2.8, y = -2.8;
printf("%g  n': pow(x, Y));
Esta funcion da como resultado la raiz cuadrada de x. Si x es negativo,
ocurre un error DOMAIN (argumento fuera del dominio de la funcion).
Las funciones de la libreria matematica Haman a la funcion matherr cuan-
do ocurre un error.
struct exception
[
int type;
char *name;
double argl, arg2;
double retval;
} .x;
tipo de error
funcion donde se origina el error
valores que causan el error
valor devuelto
DOMAIN
OVERFWW
EI argumento esta fuera del dominie de la funci6n.
EI resultado es demasiado grande para ser repre-
sentado.
Perdida PARCIAL de significaci6n.
Un argumento de la funci6n ha tornado un valor
no permitido.
Perdida total de significaci6n.
EI resultado es demasiado pequeno para ser repre-'
sentado.
PWSS
SING
TWSS
UNDERFWW
En el ejemplo siguiente se muestra la forma de utilizar la funci6n mat-
herr(), la cual sera Hamada automaticamente si"alguna funci6n matemati·
ca da lugar a un error.
# include <stdio.h>
# include <math.h >
# include <string.h >
main( )
{
struct exception *X;
double a = -2, b = 5, c = 0, Ig;
printj(H%g  n  n': log(a));
printj(H%g  n  n': loglO(b));
Ig = log(c);
if (Ig != -1) / * -1: error; valor devuelto por matherr */
printf(H%g  n  n': Ig);
int matherr(struct exception *x)
(
if (x- > type = = DOMAIN)
(
if (strcmp(x- >name, "log") = = 0)
x- > retval = log(-(x- > arg1));
else if (strcmp(x- >name, ''log10'') = = 0)
x- >retval = log10(-(x- >arg1));
printj("Arg. negativo: %s(%g)  n'~ x- > name, x- > arg1);
printf("Se calcula 19(%g): '~ -x- > arg1);
]
if (x- >type = = SING)
(
printf("Argumento no permitido.  n");
printf("log(O) = -00  n");
x- >retval = -1;
]
]
Esta fundon da como resultado un numero pseudoaleatorio entero, entre
o y 32767.
Esta funcion fija el punta de comienzo para generar numeros pseudoalea-
torios. Si no se utiliza, e1punta de comienzo siempre es el mismo para cada
ejecuci6n, que es el correspondiente a un argumento de valor 1.
Esta funcion indica el tiempo empleado por el procesador en el proceso
Hamado, en el momento que la fundon clock( ) es ejecutada. Este tiempo
es dado en milesimas de segundo. EI tiempo en segundos, es el resultado
de dividir el valor devuelto por clock( ), entre el valor de la macro
CLK_TCf(, 0 la macro CLOCK---.PER_SEC (version ANSI), ambas de
valor 1000. Si este tiempo no esta disponible 0 si su valor no puede ser
representado, la funcion devuelve un valor -1, bajo el tipo· clock_1
( (clock_t)-l ) definido de la forma: typedej long clock_t;.
Esta funcion da como resultado el numero de segundos transcurridos des-
de las 0 horas dell de Enero de 1970.
Esta funcion convierte un tiempo almacenado como un valor de tipo time_I,
a una cadena de caracteres de la forma:
Esta funci6n devuelve un puntero a la cadena de caracteres resultante;
o un puntero nulo (NULL), si t representa un dato anterior a 1980.
# include <stdio.h>
# include <stdlib.h>
# include <time.h >
main( )
[
int x, tm;
time_t segundos;
time(&segundos);
printf("  n%s  n': ctime(&segundos));
srand((unsigned)(segundos % 65536));
for (x 1; x < = 5; x+ +)
[
do / * tiempo de espera igual a 1 segundo */
tm clock( );
while (tm/CLK_TCK < x);
/ * se genera un numero aleatorio cada segundo */
printft'Iteraci6n %2d, nro. aleatorio = %d  n': x, rand( ));
J
J
Esta funci6n convierte el numero de segundos transcurridos des de la 0 ho-
ras dell de Enero de 1970, valor obtenido generalmente por la funci6n
timer ), ala fecha y hora correspondiente (corregida en funci6n de la zona
horaria en la que nos encontremos). EI resultado es almacenado en una
estructura de tipo tm, definida en time.h.
La funcion loealtime devuelve un puntero a.la estructura que contiene
el resultado, 0 un puntero nulo si el tiempo no puede ser interpretado.
tm_see
tm-'llin
tm_hour
tm_mday
tm_mon
tm_year
tm_wday
tm_yday
# include <stdio.h>
# include <time.h >
Segundos (0 - 59)
Minutos (0 - 59)
Horas (0 - 23)
Dia del mes (1 - 31)
Mes (0 - 11; Enero = 0)
Ano (actual menos 1900)
Dia de la semana (0 - 6; Domingo = 0)
Dia del ano (0 - 365; 1 de Enero = 0)
main( )
{
struet tm 4h;
time_t segundos;
timer&segundos);
fh = /ocaltime(&segundos);
printj(H%d horas, %d minutos  n': fh- > tm_hour, fh- > tm_min);
}
Esta funcion da como resultado el cociente y el resto de la division de DU·
merador entre denominador.
La funci6n div() devuelve el resultado (cociente y resto) en la estruc-
tura div_t,. definida en el fichero stdlib.h.
typedef struct _div_t
[
int quot;
int rem;
] div_t;
/ * cociente */
/* resto */
La funci6n ldiv() realiza la misma funci6n que div(), pero para valo-
res de tipo long. EI resultado es devueIto en la estructura Idiv_t, definida
en el fichero stdlib.h.
typedef struct _ldiv_t
[
long quot; / * cociente */
long rem; / * resto */
]ldiv_t;
Esta funci6n ordena un array, utilizando el algoritmo quick-sort. La fun-
cion no retorna -un valor.
comparar es un puntero a una funci6n definida por el usuario, que com-
para dos elementos y retorna un valor:
< 0 si el elemento1 es menor que el elemento2,
> 0 si el elemento1 es mayor que el elemento2
= 0 si el elemento1 es igual al elemento2.
La clasificaci6n que se obtiene es en orden ascendente. Si queremas
una clasificaci6n en orden descendente, invertir los valores devueltos par
la funci6n comparar( }, para mayor que y menor que.
# include <stdio.h>
# include <std!ib.h>
# define N 100 / * nlimero maximo de elementos para el array */
/ * necesaria para qsort. */
/ * Compara dos elementos */
ascendente, -1 = descendente */
main( ) / *funci6n principal */
[
int !ista[N]; / * array de elementos a ordenar */
int r, i = -1;
do
[
systemt 'cls"};
printjt'(, Como desea la ordenaci6n ?  n  n"};
pr,intj("Ascendente = 1, Descendente = -1 = > "};
scanf("%d': &asc_des};
}
while (asc_des != 1 && asc_des != -1);
printj("  nlntroducir !ista de nlimeros a ordenar.  n"};
printj("Fina!izar con con un caracter no numerico.  n  n "};
while (scanf("%d': &!ista[+ +i]) != 0 && i < N};
/ * Clasijicaci6n ut!izando el algoritmo Quicksort */
qsort(!ista, i, sizeof(!ista!O]), comparar);
printjt'  nLista de valores ordenada:  n  n ");
for (r = 0; r < i; r+ +)
printj("%d ': !ista!r]);
int comparar(int ~rgl, int ~rg2)
{
if (~rgl < ~rg2)
return (-asc_des);
else ij (~rgl > *arg2)
return (asc_des);
else
return (0);
Este programa lee una lista de numeros y la clasifica, utilizando la fun-
cion qsort( ), en orden ascendente 0 descendente.
La funcion qsort( ), implementada en C, recibe como panimetros: la
direccion de comienzo del array, el numero de elementos a ordenar, la lon-
gitud en bytes de cada elemento y el nombre de una funcion implementada
por el usuario para comparar dos elementos. El nombre de una funcion
esuna constante de valor, la direccion de comienzo de la funcion. Esta fun-
cion es Hamada por qsort( ) y en la Hamada Ie pasa las direcciones de los
doselementos a comparar, segun se ve en la definicion; de ahi que, en nuestro
caso,los argumentos de la funcion comparar( ) sean dos punteros a enteros.
Esta funcion ejecuta una busqueda binaria del objeto, en un array clasi-
ficado.
#include <stdlib.h > 0 <search.h >
void *bsearch(const void *objeto, const void .base, size_t num,
size_t bytes, comparar);
< 0 si el elemental es menor que el elemento2,
> 0 si el elemental es mayor que el elemento2
= 0 si el elemental es igual al elemento2.
es un puntero a una funci6n definida por el usuario, que com-
para dos elementos y retorna un valor:
Esta funci6n retorna un puntero a la primera ocurrencia de objeto en
el array apuntado por base. Si la busqueda falla, la funci6n retorna un
NULL. (Ver tambien la fund6n qsort( )."
Ifind(objeto, base, num, bytes, comparar)
Isearch(objeto, base, num, bytes, comparar)
Las funciones ljind( ) y lsearch( ), ejecutan una busqueda lineal de objeto
en un array no necesariamente clasificado. Si objeto no se encuentra sobre
el array, la funci6n lsearch() 10 afiade al final de este, mientras que ljind() no.
void *lfiild(const void *objeto, const void *base,
unsigned int num, unsigned int bytes, comparar);
void *Isearch(const void *objeto, const void *base,
unsigned int num, unsigned int bytes, comparar);
es un puntero a una funci6n definida por el usuario, que com-
para dos elementos y retorna un valor:
!= 0 si el elemental y el elemento2, son distintos,
= 0 si el elementol es igual que el elemento2.
Estas funciones retornan un puntero a la primera ocurrencia de obje-
to en el array apuntado por base. Si la busqueda falla, la funci6n lfind( )
retorna un NULL, y lsearch( ) retorna un puntero al elemento afiadido al
final del array.
# include < stdio.h >
# include <search.h>
int comparar(int *, int *); / * necesaria para lsearch. */
/ * Compara dos elementos */
/ *funci6n principal */main()
[
static int lista[ j = [24, 15, 5, 69, 43, 24, 2, 1, 8, 10, 13};
/ * n: mlmero de elementos del array */
unsigned n = sizeof(lista)/sizeof(lista[Oj);
int v; / * objeto a buscar */
int r = 0;
int *p;
system("cls ");
printj("%d  n':n);
printf(";, Valor a buscar ? ");
scanf("%d': &v);
/ * Busqueda lineal utilizando la funci6n lsearch( ) */
p = lsearch(&v,lisla, &n, sizeof(lisla[Oj), comparar);
printj(H nelemento encontrado/afiadido: %p %d n': p, *p);
/ * si el elemento es afiadido n se ve incrementado en 1 */
printj(H  nLista de valores:  n  n ");
for (r = 0; r < n; r+ +)
printf(H%p %d  n': lista +r, lista[r));
int comparar(int *argl, int *arg2)
[
if (*argl /= *arg2)
return (1);
else
return (0);
Realizar un programa para clasificar u ordenar lineas, de modo que
si se aporta el argumento -n ordene las lineas de entrada numericamente;
y si no, que las ordene lexicognificamente (alfanumericamente).
Una clasificaci6n u ordenaci6n se bas a en un algoritmo que realiza
comparaciones e intercambios hasta que 10s elementos esten ordenados. Este
algoritmo es independiente de las operaciones de comparaci6n e intercam·
bio, por 10 que pasandole diferentes funciones para comparar, y si es preci·
so, para intercambiar, podremos efectuar la ordenaci6n para distintos ti·
pos de objetos.
main( )
I
Cuando la fund6n principal maio( ) llama a la funci6n clasificar( ),
Ie pasa un puntero a la funci6n oumcmp( ), para realizar una comparaci6n
numerica de dos elementos, en el caso de que la ordenaci6n sea numerica.
En caso que la ordenaci6n sea alfanumerica, Ie pasa un puntero a la fun-
ci6n strcmp( ) de C, para realizar una comparaci6n alfanumerica de dos
elementos. En ambos cas os, cuando la funci6n maio( ) llama a la funci6n
c1asificar( ), Ie pasa un puntero a la funci6n cambiar( ), para intercambiar
el valor de dos elementos, independientemente de su tipo, pues 10 que in-
tercambiamos son las direcciones a estos objetos, en un array de direccio-
nes 0 punteros.
# include <stdio.h>
# include <conio.h >
# include <ctype.h >
# include <string.h>
# define LINEAS 100
main(iot argc, char *argv[J)
I
char *plinea[LINEAS];
iot nlineas;
iot LeerLineas (char **, iot);
void clasijicar(char **, iot, iot (*)( ), iot (*)( ));
1* strcmp es una funci6n de C (comparar lexicogrdficamente) *1
iot numcmp(char *, char *); 1* comparar numericamente *1
void cambiar(char **, char **); 1* funci6n de intercambio *1
void EscribirLineas(char *plinea[ ], iot nlineas);
char c;
I * array de punteros alas lfneas ,:,1
1* numero de lfneas lddas d
system(Hcls'');
printj(HLa utilizaci6n de este program a es:  n  n");
printjt' PROGOO.EXE -n ");
printf(HI * ordena numericamente *1 n");
printj(H PROGOO.EXE ");
printf(HI * ordena lexicogrdficamente *1 n  n ");
printf(HDesea continuar sin ");
c = getch( );
if (tolower(c) /= 's')
exit(O);
systemt 'cls");
print! ("Proceso de clasificaci6n por /(neas.  n  n");
print! ("Introducir datos. Pulse Enter para SALIR.  n");
if ((nlineas = LeerLineas(plinea, LINEAS)) > = 0)
[
print! ("Proceso de clasificaci6n.  n  n");
if (argc > 1 && argv[I][O] = = '-' && argv[l][l] 'n')
clasificar(plinea, nlineas, numcmp, cambiar);
else
clasificar(plinea, nlineas, strcmp, cambiar);
EscribirLineas(plinea, nlineas);
J
else
print! ("Demasiadas /(neas para clasificar  n ");
1**************************************************************
Funci6n leer lineas
**************************************************************/
int LeerLineas (char *plinea[ ], int lineasmax)
[
int longitud, nlineas = 0;
char *P, linea[LONGMAX];
I * Leer una linea *1
while ((longitud = strlen(gets(linea))) > 0)
{
if (nlineas > = lineasmax)
return (-1); 1* demasiadas [[neas a ordenar */
I. asignar espacio de memoria para la linea leida */
else if ((p = (char .)malloc(longitud+1)) = = NULL)
return (-1); I. insuficiente espacio de memoria */
else
(
I. copiar la lfnea en el espacio de memoria asignado */
strcpy(p, linea);
/ * guardar el apuntador a la lfnea en el array */
plinea[nlineas+ +J = p;
I
I
return (nlineas); / * mimero de lfneas lefdas */
I
/**************************************************************
Funci6n clasificar
**************************************************************/
/ * Ordena las cadenas plinea[OJ ... plinea[NdeLineas - 1J
* ascendentemente
*/
void clasificar(char *plinea[ J,iot NdeLineas,
iot (*comp)( ), iot (*per)( ))
{
char wux;
iot i, s = 1;
while ((s 1) && (--NdeLineas > 0))
{
s = 0;
for (i = 1; i < = NdeLineas; i + +)
if (( *Comp)(plinea[i-1J, plinea[i]) > 0)
{
(*per)(&plinea[i-1J, &plinea[i]);
s = 1;
1**************************************************************
Funci6n numcmp (comparar numericamente)
**************************************************************/
iot numcmp (char >tStr1,char >tStr2)
(
double atof( ), n1, n2;
nl = atof(str1);
n2 = atof(str2);
if (n1 > n2)
return (1);
else if (n1 < n2)
return (-1);
else
return (0);
1**************************************************************
Funci6n cambiar
**************************************************************/
void cambiar (char **px, char **py)
(
char wux;
aux *px;
*px *py;
*py aux;
1**************************************************************
Funci6n escribir lfneas
**************************************************************/
void EscribirLineas(char *plinea[ J, iot nlineas)
{
while (--nlineas > = 0)
printj(H%s  n': *plinea+ +);
2
Operaciones con ficheros en C
•FuncionesEstandar de E/S
•Funcionesde E/S de Bajo Nivel
•Funcionespara la Consola y Puertos de E/S
Las funciones de entrada y salida (E/S) de las librerias estandar de C, per-
miten leer y escribir datos a, y desde, ficheros y dispositivos. Un fichero
esuna colecci6n de informaci6n que almacenamos en un soporte magneti-
co,generalmente un disco, para poder manipularla en cualquier momento.
C tiene disponibles los tres tipos siguientes de funciones de E/S:
En este capitulo se presentan las funciones estandar de E/S; su carac-
teristica fundamental es que la E/S, en el procesamiento de ficheros, se rea-
lizaa traves de un buffer 0 memoria intermedia. Tambien, permiten la E/S
con formato. C se refiere a estas funciones como "Stream I/O".
La utilizaci6n de un buffer 0 memoria intermedia para realizar las ope-
raciones de E/S, es una tecnica, implementada en software, disefiada para
hacerlas operaciones de E/S mas eficientes. Un buffer es un area de datos
en la memoria (RAM)>-asignada-por el programa-qll.e bre el fichera. La
utilizacion de buffers en operaciones de E/S, reduce e1numero de accesos
al dispositivo fisico (disco por ejemplo) asociado con el fichero, necesarios
para la transferencia de informacion entre e1programa y el fichero; un ac-
ceso a un dispositivo fisico consume mucho mas tiempo que un accesoa
la memoria (RAM). Cuando un fichero no tiene asociado un buffer, cada
byte escrito a, 0 leido desde, el fichero es fisicamente transferido en el mo-
mento de la operacion. En cambio, cuando un fichero tiene asociada un
buffer, todas las operaciones de E/S requeridas son servidas desde ese buf-
fer; la transferencia fisica de datos se hace en multiplos del tamafio del buffer.
Las funciones eShindar de E/S, como su nombre indica, proporcionan la
forma mas normal de E/S en un programa C. Permiten escribir y leer da-
tos de un fichero, de las siguientes formas:
Primera, los datos pueden ser escritos 0 leidos caracter a caracter can
las funciones jputc( ) y jgetc( ).
~ - Segunda, los datos pueden ser escritos y leidos palabra a palabra can
las funciones putw() y getw(). Se entiende por palabra, palabra maquina
o valor de tipo into
3·- Tercera, los datos pueden ser escritos y leidos como cadenas de carac·
teres con las funciones jputs( ) y jgets( ).
,:. Cuarta, los datos pueden ser escritos y leidos con formato, con lasfun·
ciones jprintf( ) y jscanf( ).
Quinta, los datos pueden ser escritos y leidos como registros a blo-
ques, (esto es, como un conjunto de datos de longitud fija, tales comoes-
tructuras 0 elementos de un array), con las funciones jwrite( ) y jread(t
Para poder escribir 0 leer sobre un fichero, primeramente hay que abrirlo
con las funcionesjopen(), jdopen() 0jreopen(). El fichero puede ser abierto
para leer, para escribir 0 para leer y escribir; y puede ser abierto en modo
texto 0 en modo binario.
La necesidad de dos modos diferentes, es por las incompatibilidades
existentes entre C y MS-DOS ya que C fue disefiado original mente para
el sistema operativo UNIX. El modo texto es para ver los ficheros como
si estuvieran bajo UNIX; y el modo binario, para verlos como si estuvie-
ran bajo MS-DOS.
En modo texto, un final de linea es representado en C por un unico
canicter ('  n'), pero en un fichero de MS-DOS es representado por dos
caracteres (CR + LF). Esto significa que, bajo MS-DOS, cuando C escri-
be en un fichero convierte el canicter '  n', en los caracteres CR + LF;
y cuando C lee de un fichero y encuentra los caracteres CR + LF, los con-
vierte a '  n'; y cuando encuentra un Ctrl +Z 10 interpreta como un EOF.
En modo binario estas conversiones no tienen lugar.
Cuando un programa comienza su ejecuci6n, son abiertos automiti-
camente cinco ficheros, que se corresponden con dispositivos. Estos fiche-
ros, direccionados por streams, y los dispositivos asociados par defecto son:
~ .Ii
1,
stdin
stdout
stderr
stdaux
stdprn
dispositivo de entrada estandar (teclado)
dispositivo de salida estandar (pantalla)
dispositivo de error estandar (pantalla)
dispositivo auxiliar estandar (puerto serie)
dispositivo de impresi6n estandar (impresora paralelo)
De estos cinco, dos de ellos, el dispositivo serie y el dispositivo de im-
presion paralelo, depend en de la configuraci6n de la maquina, por 10 tan-
to pueden no estar presentes.
Las streams especificadas anterior mente, estan declaradas como pun·
teros constantes a una estructura de tipo FILE. Esta estructura define un
buffer para conectar, a traves de el, la stream con el fichero fisico. Debido
a esto en much as ocasiones nos referiremos a la stream como si fuera e
fichero. Estas streams pueden ser utilizadas en cualquier funcion que reo
quiera como argumento un puntero a un fichero. La entrada y salida es·
tandar, podran ser redireccionadas utilizando los simbolos <, >, >> a
(ver notas sobre DOS en el capitulo 19).
Despues de haber finalizado el trabajo con un fichero, este debe cerrarse
con la funcionjclose(). Si un fichero no se cierra explicitamente, es cerra·
do automaticamente cuando finaliza el programa. Sin embargo, es aeon·
sejable cerrar un fichero cuando se ha finalizado con el, ya que el numero
de ficheros abiertos al mismo tiempo esta limitado.
Las operaciones de lectura y escritura siempre empiezan en una posicion
perfectamente definida en todo momento. A esta posicion la denominare·
mos puntero de lectura escritura (LIE). Cada vez que se efectua una ope-
racion de lectura 0 de escritura, el puntero de LIE avanza a la siguiente
posicion. Cuando un fichero se abre, el puntero de LIE es posicionado auto·
maticamente al principio del fichero, excepto cuando se abre para afladir
informacion; en tal caso, es posicionado al final del fichero.
EI puntero de LIE puede ser situado en cualquier parte del fichera,
utilizando la funcion jseek( ). Para situarse al principio de un fichero se
dispone de la funcion rewind( ); para determinar en que posicion nos en·
contramos, tenemos la funcion jtell( ).
Cuando en una operacion sobre un fichero ocurre un error, este puede ser
detectado por la funcion jerror( ). Cuando ocurre un error, el indicador
de error permanece activado hasta que el fichero se cierra, a no ser que
utilieemos la funci6n clearerr( ) 0 rewind( ) para desactivarlo explicitamente.
Existen tres organizaciones de ficheros basicas, de cuya combinaci6n se de-
rivan multitud de organizaciones posibles. Estas son:
Secuencial
Aleatoria
Secuencial indexada
En cada caso, se elegira una u otra en funci6n de las caracteristicas
de los soportes y del modo de acceso requerido.
- Acceso secuencial
- Acceso aleatorio 0 directo
Se habla de acceso secuencial cuando se van accediendo posiciones
sucesivas, esto es tras acceder a la posici6n N, se accede a la posici6n N + 1;
y se habla de acceso aleatorio 0 directo cuando se accede directamente a
la posici6n deseada, sin necesidad de acceder alas posiciones que Ie
preceden.
Un fichero en C esta organizado secuencialmente y el acceso puede
ser secuencial, 0 aleatorio si utilizamos la funci6n fseek( ).
Esta funci6n abre el fichero especificado por path. El argumento acceso
especifica c6mo es abierto el fichero.
# include <stdio.h >
Abrir un fichero para leer. Si el fichero no existe 0 no se en-
cuentra, se obtiene un error.
Abrir un fichero para escribir. Si el fichero no existe, se crea;
y si existe, su contenido se destruye para ser creado de nuevo.
Abrir un fichero para afiadir informaci6n al final del mismo.
Si el fichero no existe, se crea.
Abrir un fichero para leer y escribir. Si el fichero no existe,
se crea; y si existe, su contenido se destruye para ser creado
de nuevo.
Abrir un fichero para leer y afiadir. Si el fichero no existe,
se crea.
Alas formas de acceso mencionadas, se les puede afiadir un canicter
to b (rb, a + b 0 ab +, etc.), para indicar si el fichero se abre en modo texto
o en modo binario. La opci6n t, no pertenece allenguaje C estandar; sino
que es una extensi6n de Microsoft. Si t 0 b no se especifican, el modo
(O_TEXTu O---.BINARY) es definido por la variable global-fmodede
C (O_TEXT por defecto).
La funcionjopen( ) devuelve un puntero a una estructura de tipo FILE,
la cual se corresponde con el buffer asociado con el fichero abierto. Un
puntero nulo indica un error. Este puntero es utilizado por las funciones
C, para leer y escribir datos en un fichero. Por eso antes de utilizar la fun·
ci6njopen(), debemos definir un puntero de tipo FILE, tipo que esta de-
clarado en el fichero stdio.h. Para simplificar nos referiremos a ese pun/e-
ro, diciendo que apunta al fichero abierto.
# include < stdio.h >
FILE *pj;
pf = jopen("datos': "w");
Este ejemplo indica que se abre el fichero datos para escribir, y que
sera referenciado por el puntero pf
Debe especificarse el fichero de cabecera stdio.h, porque contiene la
declaraci6n de FILE. Esta es de la forma siguiente:
struct _iobuj [
char *-ptr;
int _ent;
char *_base;
char -flag;
char -file;
);
typedef struct _iobuj FILE;
La variable pj, declarada en el ejemplo anterior, contiene la direcci6n
de memoria (puntero) de un elemento del array de estructuras _iob[ J; esta
asignaci6n ocurre, por cad a fichero que se abre. El array _iob[ J, tiene
un numero de elementos igual al valor especificado por la variable FILES
declarada en el fichero de configuraci6n del sistema, CONFIG.SYS. Como
ejemplo, observar como estan definidas las streams estandar.
# define stdin
# define stdout
# define stderr
# define stdaux
# define stdprn
(&_iob[OJ)
(&_iob[lJ)
(&_iob[2J)
(&_iob[3J)
(&_iob[4J)
Asocia una stream con un numero de fichero, num, resultante de haber
abierto el fichero con la funci6n a nivel de sistema open( ) (ver capitulo
siguiente). Esto nos permite procesar el fichero como si hubiera sido ha·
bierto por la funci6n jopen( ). La descripci6n para el argumento acceso,
es la misma que la dada en la funci6n jopen( ).
La funci6n jdopen( ) devuelve un puntero al fichero abierto por ella.
Un puntero nulo indica un error.
# include <stdio.h>
# include <jcntl.h>
# include <io.h >
FILE *pj;
iot nj;
nj = open(<<datos': O-.RDONLY);
pj = jdopen(nj, «r");
Esta funci6n cierra el fichero actualmente asociado con el puntero pf; j
reasigna pf, al fichero identificado por path. Es utilizada para redireccio·
nar stdin, stdout, stderr, stdaux y stdprn, a ficheros especificados por el
usuario. La descripci6n para el argumento acceso, es la misma que la dada
en la funci6n jopen( ).
La funci6n jreopen( ) devuelve un puntero al fichero abierto nueva·
mente. Si ocurre un error, el fichero original es cerrado y se devuelveun
puntero nulo.
# include <stdio.h >
FILE *pj;
pj = jreopen("datos': "w': stdout);
Este ejemplo, reasigna stdout al fichero Hamado datos. Ahora, 10 que
escribamos en stdout, sera escrito en datos.
Esta funci6n cierra el fichero apuntado por pf. Cualquier dato en el buffer
asociado, se escribe en el fichero antes de cerrarlo.
Si el fichero es cerrado, la funci6njclose( ) devuelve un cero. Si ocurre
un error entonces devuelve un EOP.
La funci6njcloseall() devuelve un entero igual al numero de ficheros
cerrados. Si ocurre un error, entonces devuelve un EOP.
Esta funci6n verifica si ha ocurrido un error en una operaci6n con fiche·
ros. Cuando ocurre un error, el indicador de error para ese fichero se pone
activo y permanece en este estado, hasta que sea ejecutada la funci6n clea-
rerr( ).
La funci6n jerror( ), devuelve un cero si no ha ocurrido un errory
un valor distinto de cero en caso contrario.
Esta funci6n desactiva el indicador de error y el indicador de fin de fiche-
ro (EOF) para un determinado fichero, poniendolos a valor O.
FILE *pj;
char *cadena "Esta cadena nunca sera escrita";
main( )
{
pj = jopen("datos': "r");
jprintf(pf, "%s  n': cadena);
if (ferror(pf))
{
jprintj(stderr, "Error de escritura n");
clearerr(pf);
J
jclose(pj);
J
Este programa abre el fichero llamado datos para leer y a continua-
cion se intenta escribir. La funci6njerror() detecta el error, manda un men-
saje por la consola y desactiva el indicador de error para ese fichero.
La funci6n jeof( ) devuelve un valor distinto de 0, cuando se intenta
leer un elemento del fichero y nos encontramos con un eof (end of file -
fin de fichero). En caso contrario devuelve un O.
while (!feof(pf))
!
printj ("Denominacion:
printj ("Precio:
%s  n': reg.denomi);
%d  n  n': reg.precio);
/ * Leer el siguiente registro del jichero */
jread (&reg, sizeof(struct registro), 1, pf);
J
jclose(pf);
Este ejemplo dice: mienttas no se llegue al final del fichero, leer regis-
tros y escribirlos por la pantalla. Los registros leidos son estructuras. Des-
pues de haber leido el ultimo registro, observamos que es necesario hacer
una nueva lectura para activar el indicador eof.
if ((pj = jopen("datos': "r"))
perror("Fichero no abierto");
Esta funci6n escribe en la salida estandar stderr, el mensaje dado par ca·
dena seguido por dos puntos, el mensaje de error dado por el sistema)
un caracter NL.
EI numero de error es almacenado en la variable del sistema errn~
la cual seria declarada a nivel externo. La variable del sistema sys_erriisl
es un array que contiene los mensajes de error ordenados por el numero
de error. La funci6n perror( ) busca el mensaje de error en esta variable
utilizando el valor de la variable errno como indice.
int errno;
int sys_nerr;
char *sys_errlist[sys_nerr];
Esta funci6n escribe un caracter car en la posici6n indicada por el puntero
de LIE del fichero apuntado por pf.
La funci6n jpute( ), devuelve el canicter escrito 0 un EOF si ocurre
un error. No obstante, ya que EOF es un valor aceptado por car, utilizar
la funci6n jerror( ) para verificar si ha ocurrido un error.
La macro pute desarrolla la misma funci6n y de la misma forma que
la fund6n jpute( ).
# include <stdio.h >
# include <string.h >
FILE *pj,o
char bujjer[81},o
int i = 0;
main( )
(
/ *Abrir ef jiehero "texto" para escribir */
if ((pj = jopen("texto': "w")) = = NULL)
(
perror("Ef jiehero no se puede abrir"),o
exit(l),o
J
strepy(bujjer, "Este es un texto para jpute!!  n "),o
while (fjerror(pf) && bujjer[i])
jpute(bujjer[i + +}, pf),o
if (ferror(pf))
perror("Error durante fa eseritura "),o
jclose(pj),o
J
/ * Abrir el fichero "texto" para leer */
if ((pf = Jopen("texto'~ "r")) = = NULL)
{
perror("EI fichero no se puede abrir");
exit(l);
J
while (fjerror(pf) && fjeof(pf))
buffer[i + +] = fgetc(pf);
buffer[--i] = C  0';
Esta funci6n lee un cankter, del fichero apuntado por pf, de la posicion
indicada por el puntero de LIE.
La funci6n fgetc( ) devuelve el canicter leido 0 un EOF si ocurre un
error 0 se detecta el final del fichero. No obstante, ya que EOF es un valor
aceptado, utilizar la funci6n ferror( ) 0 feof( ) para distinguir si se ha de·
tectado el final del fichero 0 si ha ocurrido un error.
La macro getc desarrolla la misma funci6n y de la misma forma que
la funci6n fgetc( ).
FILE *pf;
char buffer[81J;
iot i = 0;
if (ferror(pf))
perror(<eError durante la lectura ");
printjt'%s': bujjer);
l
El siguiente programa lee el texto contenido en un fichero pasado como
argumento en la linea de 6rdenes, y da como resultado el numero de carac-
teres de dicho fichero.
/************** Contar los caracteres de un jichero **************/
/ * CCONTA.C */
# include <stdio.h>
# include <stdlib.h>
main(int argc, char *argv[J)
(
FILE *pj;
int conta = 0;
system(<ecls");
/ * Comprobar el nOde argumentos pasados en la linea de ordenes
*/
if (argc /= 2)
(
printf(<ePormato: C> cconta nombre-fichero  n");
exit(l);
l
/ * Abrir el jichero indicado por argv[l] */
if ((pj = jopen(argv[l], <er")) = = NULL)
(
printj(<eEIjichero %s no puede abrirse  n': argv[IJ);
exit(l);
while (fjerror(pf) && fjeof(pj))
[
}
if (ferror(pj))
perror(UError durante la lectura ");
jclose(pf);
print/fuEl jichero %s tiene %d caracteres  n': argv[l],conta-l);
}
Esta funci6n escribe un valor binario entb de tipo int, en el fichero apun-
tado por pf.
La funci6n putw( ) devuelve el valor escrito 0 un EOP si ocurre un
error. No obstante, ya que EOF es un valor valido, utilizar la funci6n fe-
rror( ) para verificar si ha ocurrido un error.
Esta funci6n lee el siguiente valor binario de tipo int, del fichero apuntado
por pf y avanza el puntero de LIE al siguiente valor no leido.
La funci6n getw( ) devuelve el valor leido 0 un EOF si ocurre un error
o se detecta el final del fichero. No obstante, ya que EOF es un valor vali-
do, utilizar la funci6n jerror( ) 0 jeof( ) para distinguir si se ha detectado
el final del fichero 0 si ha ocurrido un error.
main( )
[
static int !ista[ J
int elementos
int i;
= { -1, 10, 20, 30, 40, 50 };
sizeof (!ista)Isizeof (int);
1* Abrir el jichero para leer y escribir *1
pf = jopen(Hdatos.bin': HW+ ");
1* Escribir el array de enteros en el jichero *1
for (i = 0; i < elementos; i+ +)
[
putw(!ista[i], pj);
if (ferror(pf))
{
perror(HError durante la escritura");
exit(l);
}
J
1* Posicionar el puntero de LIE al principio *1
rewind(pj);
1* Escribir el contenido del jichero *1
while (1)
[
i = getw(pf);
if (feoj(pj) II jerror(pf))
break;
printf(H%d ': i);
1
if (ferror(pf))
perror(HError durante fa fectura");
jclose(pf);
1
Este programa escribe en el fichero datos.bin, en binario, el contenid
de un array de enteros llamado !ista; y a continuaci6n visualiza el conteni
do de dicho fichero. Observar que para escribir el contenido del ficher
primeramente hay que situar el puntero de LIE al principio del mismo.
Esta funci6n copia la cadena de caracteres, cadena, en el fichero apuntado
por pf. La terminaci6n '  0' no se copia.
La funci6njputs(), si no hay error, devuelve un O. En caso contrario,
devuelve un valor distinto de O.
Para recuperar de una forma sencilla la informaci6n escrita en el fi·
chero, es aconsejable copiar el canicter '  n' despues de cada cadena es·
crita sobre el fichero.
while (gets(cadena) != NULL)
{
jputs(cadena, pj);
jputc('  n: pj);
1
Esta funci6n lee una cadena de caracteres, cadena, del fichero apuntado
por pf. La terminaci6n '  0' es afiadida automaticamente a la cadena lei-
da. Se entiende por cadena desde la posici6n actual dentro del fichero, hasta
el primer canicter nueva linea ('  n') incluido este, hasta el final del fiche-
ro, 0 hasta que el numero de caracteres sea igual a n-1.
La funci6n fgets( ) devuelve la cadena leida. Si el valor devuelto es
NULL, quiere decir que ha ocurrido un error 0 que se ha detectado un
EOP. Utilizar feof( ) 0 ferror( ) para determinar 10 que ha ocurrido.
# include <stdio.h>
# include <stdlib.h>
# define N 81
main( )
(
FILE *pf;
char buffer[NJ, f[13J;
system (Hcls");
printf(HNombre del fichero: ");
gets(f);
/[12J = ' 0'; / * truncar nombres superiores a 12 caracteres */
/ * Abrir en modo binario el fichero f para escribir y leer */
if ((pf = fopen(j, HW+ b")) = = NULL)
(
printj(HEI fichero O/OS no puede abrirse:: f);
exit(l);
)
printj(HFichero O/OS abierto  n': f);
while (gets(bujjer) != NULL)
(
jputs(bujjer, pf);
if (ferror(pf))
(
perror("Linea demasiado larga");
exit(2);
l
jputct n~ pj);
l
printjt'Introducir datos. Finalizar cada linea con Enter  n");
printj("La entrada de datos jinalizard con etrl +Z  n  n");
/ * Visualizar el contenido del jichero */
rewind(pj); / * situarse al principio del jichero */
/ * leer hasta un ' n' 0 hasta N-l caracteres */
while (fgets(bujjer, N, pj) != NULL)
printf("%s'~ bujjer);
if (ferror(pf))
perror("Error durante la lectura");
jclose{pj);
l
Este programa lee lineas de texto y las almacena en un fichero. Cada
linea en el fichero, va seguida del canicter '  n~ Finalmente se visualiza
el contenido del fichero creado.
EI siguiente ejemplo muestra como lanzar un resultado a la impresora pa-
ralelo. Concretamente este programa escribe el contenido de un fichero de
texto, por la impresora. La orden de ejecucion sera de la forma: cprint
nombre_fichero, donde cprint es el nombre de nuestro programa y
nombre_fichero es el fichero que queremos escribir por la impresora.
/****** Escribir el contenido de un jichero por la impresora ******/
/ * CPRINT.C */
#include <stdio.h >
# include <stdlib.h>
#dejine N 81
main(int argc, char *Grgv[ ])
{
FILE *pj;
char bujjer[N];
if (argc /= 2) / * chequear el numero de argumentos */
{
printf(HPormato: C> cprint nombre-Jichero.  n ");
exit(l);
l
/ * Abrir el jichero indicado por argv[l] para leer */
if ((pj = jopen(argv[l], Hr")) = = NULL)
{
printf(HEI jichero O/OS no puede abrirse.  n': argv[lJ);
exit(2);
l
/ * Escribir el contenido del jichero por la impresora */
while (fgets(bujjer, N, pj) / = NULL)
jputs(bujjer, stdprn);
fclose(pj);
l
Observar la sentenciajputs(bujjer, stdprn); para que la salida se pro-
duzca par la impresara, basta especificar en la funci6n utilizada para es-
cribir, que el fichero de salida es el apuntada par stdprn (impresara paralela).
Esta funcion escribe sus argumentos (arg) en el fichero apuntado por pf,
con el formato especificado. La descripcion de formato, es la misma que
se especifico para printf( )
La funcion jprintf( ) devuelve el numero de caracterl2S escritos 0 un
valor negativo si ocurre un error.
Esta funcion lee sus argumentos (arg) del fichero apuntado por pf, con el
formato especificado. La descripcion de formato es la misma que se espe-
cifico para scanf( ). Cad a argumento arg, debe ser un puntero a una varia·
ble en la que queremos almacenar el valor lefdo. El tipo de cad a una de
estas variables debe corresponderse con la especificacion de formato indio
cada para cada variable.
La funcion jscanf( ) devuelve el numero de argumentos que han sido
lefdos y asignados. Si el valor devuelto es un 0, significa que no se han
asignado val ores; y si es un EOp, significa que se ha detectado el final del
fichero.
# include <stdio.h>
# include <stdlib.h >
main( )
!
char bujjer[128];
FILE *ptabla;
long entl, total_ent/,'
float real, total_real;
int i, c = ~';
/ * Abrir un jichero para leer. Si no existe se crea. */
if ((ptabla = jopen("tabla.d': "r")) /= NULL)
!
/ * Leer datos del jichero y totalizarlos */
printf("RESULTADOS:  n  n");
for (i = 0, total_entl = 0, totaLJeal = 0.0; i < 10; i+ +)
!
jscanj(ptabla, "%s %c: %ld %1': bujjer, &c, &entl, &real);
total_entl + = ent/,'
total_real + = real;
printf(" t%s %c: %71d %9.2j n': bujjer, c, entl, real);
l
printf("  n  tTotal: % 71d %9.2j n': total_entl, total_real);
l
else
!
/ * Si el jichero no existe 10 creamos. */
if ((ptabla = jopen( "tabla.d': "w")) = = NULL)
exit(I);
/ * Se escribe la tabla deseada en el jichero. */
for (i = 0, entl = 99999, real = 3.14; i < 10; i+ +)
jprintj(ptabla, " tLinea %c: % 71d %9.2j n':
c+ +, entl/= 2, real * = 2);
printf("EI jichero no existia y 10 hemos creado.  n ");
printf("  nEjecuta de nuevo el programa.  n");
l
jclose(ptabla);
l
Es importante conocer c6mo la funci6njprintj( ) almacena los datos
sobre el disco. Los caracteres son almacenados uno por byte y los numeros
enteros y reales en lugar de ocupar 2, 4 u 8 bytes dependiendo del tipo,
requieren un byte por cada digito. Por ejemplo el numero 105.56 ocuparfa
6 bytes.
Cuando la cantidad de datos numericos a almacenar es grande, esta
funci6n no es la id6nea, ya que se ocupa mucho espacio de disco. La solu·
ci6n a este problema, la presentamos en el apartado siguiente.
Esta funci6n escribe hasta c elementos de longitud n bytes, almacenados
en el buffer, en el fichero apuntado por pf.
La funci6njwrite() devuelve el numero de elementos actualmente es·
critos, que puede ser menor que c si ocurre un error.
Esta funci6n lee hasta c elementos de longitud n bytes, del fichero apunta-
do por pf, y los almacena en el buffer.
La funci6njread() devuelve el numero de elementos actualmente lei-
dos, que puede ser menor que c si ocurre un error. Utilizar las funciones
jeoj( ) 0 jerror( ) para distinguir si se ha detectado el final del fichero 0
si ha ocurrido un error. Si n 0 c son 0, jread( ) devuelve un 0 y el contenido
del bujjer permanece igual.
# include <stdio.h>
# include <stdlib.h>
main( )
[
typedef struct r registro;
struct r
[
char rejerencia[20};
long precio;
];
registro reg;
int bytesreg = sizeof(reg);
FILE *pj;
char sprecio[10}, respuesta;
1* tipo regiC)°tro';</
1* dejinicion de un registro d
1* tamano de un registro d
1* puntero al jichero ,J
/ * abrir el jichero "datos" para escribir "wb" *1
pf = jopen("datos': "wb");
system ("cls "); 1* borrar pantalla */
/ * Entrada de datos */
printj ("Pulse Orl +Z para jinalizar  n  n ");
print! ("Rejerencia: ");
while (gets(reg.rejerencia) /= NULL)
(
printj ("Precio: ");
gets(sprecio); reg.precio = atol(sprecio);
/ * Escribir un registro en el jichero *1
fwrite (&reg, bytesreg, 1, pf);
print! ("  nRejerencia: ");
]
1* cerrar el jichero d
1* desactivar el indicador eoj de stdin d
fclose(pj);
clearerr(stdin);
do
[
print! t'iDesea visualizar el jichero? (sin) ");
/ * Leer el primer registro del jichero */
jread (&reg, bytesreg, 1, pi);
while (!feoj(pj))
{
printj ("Rejerencia:
printj (HPrecio:
%s  n': reg.rejerencia);
%ld  n  n': reg.precio);
respuesta = tolower(getchar( ));
jjlush(stdin);
}
while ((respuesta!= 's') && (respuesta!= en'));
/ * Salida de datos */
if (respuesta = = 's')
(
system(Hcls' ');
/ * abrir el jichero Hdatos" para leer Hrb" */
pj = jopen(Hdatos': "rb");
/ * Leer el siguiente registro del jichero */
jread (&reg, bytesreg, 1, pi);
}
}
jclose(pi);
}
Este ejemplo lee registros formados por dos campos, rejerencia ypre-
cio, y los almacena en un fichero llamado datos. Una vez creado el fichera,
disponemos de la opci6n de visualizar el fichero, registro a registro.
La funci6n jwrite( ), almacena los datos numericos en formate bina-
rio. Esto quiere decir que un int ocupa 2 bytes, un long ocupa 4 bytes, un
jloat ocupa 4 bytes y un double ocupa 8 bytes. No hay que confundir el
formato binario empleado para almacenar un dato numerico, con el modo
binario en el que se abre un fichero, 10 cual s6lo indica que se va a efectuar
una conversi6n de los finales de linea y del final de fichero.
Con ficheros abiertos en modo texto, pueden producirse resultados ines-
perados debido a la conversi6n de '  n' en CR + LF. Par ella, es aconseja-
ble trabajar en modo binario.
CONTROLDE LA MEMORIA INTERMEDIA ASOCIADA CON UN
FICHERa
Esta fund6n permite al usuario controlar la memoria intermedia asignada
al fichero apuntado por pf.
La fund6n setbuf( ) debe ejecutarse una vez abierto el fichero y antes
de cualquier operad6n de lectura 0 escritura. Si el argumento buffer es
NULL, no se Ie asigna una memoria intermedia al fichero. En caso con-
trario, buffer es un array de caracteres de longitud BUFSIZ, constante de-
finida en stdio.h. Esta memoria intermedia sera utilizada en lugar de la
asignada por defecto por el sistema.
Esta funci6n permite al usuario controlar la existencia 0 no de un buffer,
y el tamafio del mismo. La funci6n setvbuf() debe ejecutarse una vez abierto
el fichero y antes de cualquier operaci6n de lectura 0 escritura.
El argumento buffer es un array de caracteres de longitud n bytes que
desempefia la funci6n de buffer 0 memoria inter media; el argumento tipo
puede ser:
La fundon setvbuf( ) devuelve un 0 si no ocurre un error; y un valor
distinto de 0, en caso contrario.
# include <stdio.h >
# include <stdlib.h>
# include <time.h >
int cuenta_lineas(FILE *pf); / * funci6n prototipo */
FILE * abrir(char *); / * funci6n prototipo */
char bufl[BUFSIZ], buf2[MIBUFFER]; / * buffers para el fichero */
main(int argc, char *argv[])
{
time_t t_inicial;
FILE *pf;
int c;
if (argc != 2) /* chequear el mlmero de argumentos */
(
printf("Formato: C> nombre-programa nombre-fichero.  n").'
exit(l);
l
pf = abrir(argv[l]);
/************************************************************
Utilizando el buffer buf1, de tamano BUFSIZ
setbuf(pj; buf1);
t_inicial = clock( );
c = cuenta_lineas(pf);
printf("Tiempo: %5.1f tTamano del Buffer: %4d  n':
((f1oat)clock( )-t_inicial)/CLK_TCK, BUFSIZ);
Utilizando el buffer buf2, de tamano MIBUFFER
************************************************************/
setvbuf(pj; buf2, ~OFBF; sizeof(buf2));
t_inicial = clock( );
c = cuenta_lineas(pft
printf("Tiempo: %5.1f tTamano del Buffer: %4d t mi buffer  n':
((f1oat)clock( )-t_inicial)/CLK_TCK, MIBUFFER);
No utilizando un buffer
***********************-************************************/
setvbuf(pj;NULL, ~ONBF; 0);
t_inicial = clock( );
c = cuenta_lineas(pf);
printj('Tiempo: %5.1f tTamano del Buffer: %4d n':
((f1oat)clock( )-t_inicial)/CLK_TCK, 0);
printf("  nEIfichero %s tiene %d /(neas n': argv{l), c);
1
Contar /(neas en un fichero de texto
**************************************************************/
int cuenta_lineas(FILE *pf)
I
#dejineN 81
char linea_buf{N};
while (!feof(pj))
(
jgets(linea_but N, pf);
c+ +;
putchart. ');
J
putchart  n ');
jclose(pj);
return c;
/ * lee una Hnea */
/ * contar lfneas */
FILE wbrir(char 4ichero)
{
FILE *pt·
if ((pj = jopen(fichero, "r")) = = NULL)
(
print/teEI jichero %s no puede abrirse.  n': jichero);
exit(2);
J
return Pt·
J
Este programa imprime los tiempos empleados en realizar una opera·
cion de contar lineas de un texto. Primero, asigna un buffer al fichero de
tamafio fijado por el sistema; segundo, asigna un buffer al fichero de ta·
mafio fijado por el usuario; y tercero, sin asignar un buffer al fichera. La
prueba mas satisfactoria, es la segunda; en ella el tamafio del buffer es mayor.
Esta fundon escribe en el fichero apuntado por pf, el contenido del buffer
asociado al mismo. Si el fichero en lugar de estar abierto para escribir esta
abierto para leer, jjlush( ) borra el contenido del buffer.
La funci6njjlush() devuelve el valor 0 si no ocurre un error y un EOF
en caso contrario.
Esta funci6n crea un fichero temporal. Este fichero es automaticamente
borrado cuando el programa termina normalmente, 0 cuando se borra ex-
plicitamente con la funci6n rmtmp( ). El fichero temporal es abierto en
modo w+b.
La funci6n tmpjile( ) devue1veun puntero al fichero temporal creado,
o un puntero nulo si no es posible crearlo.
FILE *pj;
if ((pj = tmpjile()) = = NULL)
printf(HNo se puede abrir un jichero temporal");
Esta funci6n es utilizada para borrar todos los ficheros temporales crea-
dos por tmpfile( ), existentes en el directorio actual.
La funci6n rmtmp( ) devuelve el numero de ficheros cerrados y bl}
rrados.
Esta funci6n genera una cadena de caracteres que puede utilizarse como
nombre para un fichero. La cadena generada es unica; de tal modo que
no existe peligro de sobreescribir un fichero existente. Si el argumento ca·
dena es NULL, el resultado es almacenado en un buffer interno. El nom·
bre completo esta formado por el camino especificado por la macro
P_tmpdir mas la cadena generada. Para cambiar de directorio, modificar
el contenido de esta macro, la cual se encuentra definida en stdio.h.
La funci6n tmpnam( ) devuelve un puntero a la cadena de caracteres
generada 0 un puntero nulo si el nombre existe 0 no puede generarse. La
funci6n genera nombres diferentes de al menos L_tmpnam caracteres, hasta
un numero de TMP-MAX. Estas macros se encuentran definidas en
stdio.h.
FILE *pfl, *pf2;
char *nombrel, nombre2[L_tmpnam};
/ * Crear un nombre para un fie hero utilizando
* un buffer interno
*/
if ((nombrel = tmpnam(NULL)) /= NULL)
printj("%s es un nombre para un fiehero  n': nombrel);
/ * Crear un nombre para un jichero utilizando
* un bujjer externo
*/
if (tmpnam(nombre2) / = NULL)
printj("%s es un nombre para un jichero  n': nombre2);
Esta funci6n genera una cadena de caracteres que puede utilizarse como
nombre para un fichera. La cadena generada es unica. EI nombre com ple-
to esta formado por el camino especificado por dir, mas el nombre delfi-
chero compuesto por el prefijo pref y la cadena generada.
La fund6n tempnam( ) devuelve un puntera a la cadena de caracteres
generada 0 un puntero nulo si el nombre existe 0 no puede generarse.
FILE *pj3;
char *nombre3;
/ * Crea un nombre con el prejiJo "temp" y 10 coloca en el
*primer directorio existente de estos tres:
* 1. directorio indicado por la variable de entorno TMP
* 2. C:  TEMP
* 3. directorio P_tmpdir (dejinido en stdio.h)
*/
if ((nombre3 = tempnam("C:   TEMP': "temp")) /= NULL)
printft'%s es un nombre para un jichero  n':nombre3);
Esta funci6n, mueve el puntero de LIE asociado con el fichero apuntado
por pf, a una nueva 10calizaci6n desplazada desp bytes de la posici6n dada
por el argumento pos.
La funci6n jseek( ) devuelve un 0 si no se ha producido un error y
un valor distinto de 0 en caso contrario.
Este ejemplo situa el puntero de LIE, al principio del fichero apunta-
do por pjichero. Observar que el desplazamiento es 0 y esta expresado como
un valor long (L).
Para ficheros abiertos en modo texto, jseek( ) puede producir un re-
sultado inesperado debido a la conversi6n de '  n' en CR + LF.
Por 10 tanto en modo texto, las operaciones con la funci6n jseek( )
seran buenas con un desplazamiento 0 (ir al principio 0 al final) 0 can un
desplazamiento devuelto par la funci6n jtell( ), a partir del camienzo del
fichero.
Con el fin de no obtener resultados inesperados, es aconsejable traba-
jar en modo binario.
Esta funci6n da como resultado la posici6n actual del puntero de LIE, dentro
del fichero apuntado por pf. Esta posici6n es relativa al principio del fichero.
La funci6n jtell( ) devuelve la posici6n actual del puntero de LIE, 0
el valor -lL si ocurre un error.
long pas;
pas = jtell(pf);
Este ejemplo almacena en la variable pas, la posici6n actual del pun-
tero de LIE del fichero apuntado por pf
Esta funci6n pone el puntero de LIE del fichero apuntado por pf, al co-
mienzo del mismo.
con la excepci6n de que rewind( ) desactiva los indicadores de error y de
fin de fichero, y jseek( ) no.
I* variable de tipo registro*
1* tamaflO de un registro*
1* puntero al jichero ,
1* nO total de registros,
1* nOde registro,
1* desplazamiento en bytes*
# include <stdio.h>
# include <stdlib.h >
main( )
[
typedef struct r registro;
struct r
[
char rejerencia[20};
long precio;
];
registro reg;
int bytesreg sizeof(reg);
FILE *pj;
int totalreg;
int nreg;
long desp;
int c, respuesta;
char sprecio[IO};
1* tipo registro,
1* dejinicion de un registro*
1* abrir el jichero "datos" para leer y escribir "r+b" *1
pj = jopen("datos'; "r+ b");
1* Calcular el n° total de registros del jichero *1
jseek(pj, OL, SEEK--END);
totalreg = jtell(pj)lbytesreg;
1* Presentar un registro en pantalla *1
do
[
system("cls"); 1* borrar panta/la '!
printjt'N° registro entre 1 y %d (0 para salir): ';totalreg);
c = scanj("%d'; &nreg);
jjlush(stdin);
if (c && (nreg > = I) && (nreg < = totalreg))
[
desp = (long)(nreg - I) * bytesreg;
jseek(pf, desp, SEEK_SET);
jread(&reg, bytesreg, I, pf);
printf(H  nRejerencia: %s  n': reg.rejerencia);
printf(rcprecio: %ld  n  n': reg.precio);
1* Modificar el registro seleccionado *1
do
[
printj (''(,Desea modificar este registro? (sin) ");
respuesta = tolower(getchar( ));
jjlush(stdin);
J
while ((respuesta /= es') && (respuesta /= en'));
if (respuesta = = es')
[
printj (rc  nRejerencia: ");
gets(reg.rejerencia);
printj (rcprecio: ");
gets(sprecio); reg.precio = atol(sprecio);
1* Escribir un registro en el jichero */
jseek(pf, -bytesreg, SEEK_CUR);
jwrite (&reg, bytesreg, I, pj);
J
J
1
while (nreg);
jclose(pf);
1
Este programa ilustra el uso de las funcionesjseek(), jtell() y rewind( ).
El programa abre un fichero Hamado datos con registros de tipo registro,
calcula el numero de registros del fichero, presenta en pantaHa un determi-
nado registro previamente seleccionado, y pregunta si se desea modificar.
Esta funci6n pone el puntero de LIE correspondiente al fichero apuntado
por pf, en la posici6n indicada por pos; generalmente, este valor habra side
obtenido previamente por la funci6njgetpos( ); aunque tambien puede ser
un valor calculado. La ejecuci6n de esta funci6n, desactiva el indicador
de fin de fichero y anula el efecto de cualquier funci6n ungetc( ) previa.
La funci6n jsetpos( ) devuelve un 0 si no ocurre un error y un valor
distinto de 0, en caso contrario.
long desp;
desp = (Iong)((numreg - 1) * bytesreg;
jseek(pf, desp, SEEK_SET);
jpos_t desp;
desp = (fpos_t)((numreg - 1) * bytesreg;
jsetpos( pf, &desp);
Esta funci6n almacena en el objeto apuntado por pos, la posici6n del pun-
tero de LIE del fichero apuntado por pf. Posteriormente podremos utili-
zar la funci6n jsetpos( ) para restaurar la posici6n de este puntero.
La funcion fgetpos( ) devue1veun 0 si no ocurre un error y un valor
distinto de 0, en caso contrario.
Basandonos en el fichero datos del ejemplo anterior, desarrollar una
funcion buscar( ) que nos devuelva como resultado el numero del registro
para un dato referencia (ref) dado.
int buscar(FILE *pf, char *ref)
I
jpos_t desp;
/ * Situarse al principio del fichero *1
desp = (fpos_t)O;
jsetpos( pf, &desp);
do
I
jread(&reg, bytesreg, 1, pf);
I
while (strncmp(reg.referencia, ref, 4));
jgetpos(pf, &desp);
printf( " nRegistro %d  n  n': desplbytesreg );
printj ("Referencia: %s  n': reg.referencia);
printj (<<Frecio: %ld  n  n': reg.precio);
return(desplbytesreg);
I
Esta funcion abre e1fichero especificado por path y permite compartirlo
por otros procesos. El argumento acceso especifica como es abierto el fi-
cheroy el argumento sh es una constante que indica los atributos para com-
partir el fichero.
# include <stdio.h >
# include <share.h >
acceso: r, w, a, r+, w+, a+
Para indicar d tipo de conversion, pueden aiiadirse 10s caracteres
to b.
sh: SH_COMPAT, SH~ENYNO, SH~ENYRD,
SH~ENYRW, SH~ENYWR
La funcion -fsopen( ) devuelve un puntero a una estructura de tipo
FILE, la cual se corresponde con el fichero abierto. Un puntero nul0 indio
ca un error.
Si SHARE.COM (0 SHARE.EXE) no esta instal ado, MS-DOS igno·
ra el modo compartido.
Las funciones de E/S de bajo nivel, denominadas en C "Low-level I/O func-
tions", ofrecen po cas formas para manipular los datos. No utilizan una
memoria intermedia (buffer) para las operaciones de entrada salida, y tam-
poco pueden dar formato a los datos. Las declaraciones para estas funcio-
nes, se dan en los ficheros: io.h, sys/types.h, sys/stat.h, fcntl.h, y errno.h.
Las funciones que vamos a ver en este capitulo no pertenecen al C estan-
dar (ANSI).
Las funciones .de E/S de bajo nivel son, generalmente, incompatibles con
las funciones de E/S estandar. Las funciones de E/S estandar pueden pro-
cesar ficheros que tengan, 0 no, asociado un buffer; mientras que las fun-
ciones de E/S debajo nivel s610 pueden procesar ficheros que no tengan
asociado un buffer. Si durante el proceso de un fichero utilizamos ambos
tipos de funciones, el fichero no debe tener asociado un buffer. Cuando
un fichero no tiene asociado un buffer, cada byte escrito a, 0 leido desde
el fichero, es fisicamente transferido en el momenta de la operaci6n.
stdin
stdout
stderr
stdaux
stdprn
dispositivo de entrada estandar (teclado)
dispositivo de salida estandar (pantalla)
dispositivo de error estandar (pantalla)
dispositivo auxiliar estandar (puerto serie)
dispositivo de impresi6n estandar (impresora
paralelo)
Para poder escribir 0 leer sobre un fichero, primeramente hay que abrirlo
con las funciones open ( ), sopen( ) 0 creat( ). El fichero puede ser abierto
para leer, para escribir 0 para leer y escribir y puede ser abierto en modo
texto 0 en modo binario.
Estas funciones devuelven un mimero denominado descriptor de fi-
chero, que sera utilizado mas tarde para referirse al fichero en las opera-
ciones de lectura/escritura. El descriptor asignado a una determinada stream,
puede saberse por medio de la funci6n jileno( ).
Cuando un programa comienza su ejecuci6n, cinco ficheros, que se
corresponden con dispositivos, son abiertos automaticamente. Estos ficheros,
direccionados por streams, y los dispositivos asociados por defecto son:
De estos cinco, dos de ellos, el dispositivo serie y el dispositivo de im-
presi6n paralelo, dependen de la configuraci6n de la maquina, por 10 tan-
to pueden no estar presentes.
Despues de haber finalizado el trabajo con un fichero, hay que cerrarlo
con la funci6n close(). Si un fichero no se cierra explicitamente, es cerrado
automaticamente cuando finaliza el programa. Sin embargo, es aconseja-
ble cerrar un fichero cuando se ha finalizado con el, ya que el numero de
ficheros abiertos al mismo tiempo esta limitado.
Las operaciones de lectura y escritura se realizan por medio de las funcio-
nes read() y write(). Siempre empiezan en la posicion sefialada por el de-
nominado puntero de lectura escritura (LIE).
La posicion de LIE puede ser situada en cualquier parte del fichero,
utilizando la funcion lseek(). Para determinar en que posicion nos encon-
tramos tenemos la funcion leU( ).
Esta funcion abre el fichero especificado por path para el tipo de opera-
cion indicado por modo.
#include <fcntl.h >
# include <io.h >
# include <sys  types.h >
# include <sys  stat.h >
# include <errno.h >
El argumento modo es una expresion entera formada por una 0 mas
constantes separadas por el operador OR (I). Estas constantes definidas
en fcntl.h son las siguientes:
Abre un fichero para afiadir informacion al final del
mismo.
Crea un nuevo fichero para escribir. Si el fichero exis-
te, esta con stante no tiene efecto.
Devuelve un numero de error (variable errno) si el
fichero especificado existe. Solamente se utiliza con
O_CREAT.
Abre un fichero s610 para leer. Esta constante no se
puede utilizar con las constantes:
O_RDWR 0 O_WRONLY.
Abre un fichero para leer y escribir. Esta constante
no se puede utilizar con las constantes:
O~DONLY 0 O_WRONLY.
Abre un fichero existente, destruyendo su conteni-
do. El fichero debe tener permiso de escritura.
Abre un fichero solo para escribir. Si el fichero no
existe, no se crea y se produce un error. Esta cons-
tante no se puede utilizar con las constantes:
O_RDONLY 0 O_RDWR.
Para indicar el modo de acceso, debe especificarse al menos una de
estas constantes: O_RDONLY, 0_ WRONLY, 0 O_RDWR.
El argumento pmodo solamente es necesario con la constante
O_CREAT, para indicar el permiso dado al fichero. Si el fichero existe,
este argumento es ignorado.
Estas constantes estan definidas en el fichero sys  stat.h. Este fiche-
ra, debe incluirse despues del fichero sys  types.h; ya que las declaracio-
nes que conti ene, necesitan de los tipos definidos en este ultimo.
No es posible asignar bajo DOS a un fichero, el atributo de s610escri-
tura. Comos todos los ficheros tienen permiso de lectura, los permisos
S_IREAD I S_IWRITE y S_IWRITE son equivalentes.
Bajo las versiones de DOS 3.0 0 posteriores y con la orden SHARE
instalada, hay problemas si se abre un fichero en el modo O_CREAT I
O-l{DONLY 0 en el modo O_CREAT I 0_WRONLY y con un valor
S_IREAD para pmodo. La soluci6n al problema que surge (se cierra pre-
maturamente el fichero), es abrir el fichero en el modo O_CREAT I
O-l{DWR.
La funci6n open ( ) retorna un numero (descriptor de fichero) que ha
sido asociado al fichero abierto, para dirigirse a el en posteriores operacio-
nes de lectura 0 escritura. Un valor -1 indica un error y la variable errno
es puesta a uno de los siguientes valores:
EACCES
EEXIST
EMFILE
ENOENT
EINVAL
Permiso denegado
Fichero existe. O_CREAT y O~XCL son especificados
Demasiados ficheros abiertos
No existe tal fichero 0 directorio
Argumento invalido
El numero devuelto por la fund6n open( ), se corresponde con eli
dice del elemento del array de estructuras _iob[ J, utilizado para el fich
ro abierto (ver funci6n fopen( ).
# include <fcntl.h>
# include <sys  types.h >
# include <sys  stat.h >
# include < io.h >
# include <stdio.h >
main( )
(
int nfl, nf2,'
nf1 = open(Hdatosl': O.-RDONLY);
if (nf1 = = -1)
perror(Herror al abrir el ficheroH
);
else
printftjichero abierto para leer  n");
nf2 = open("datos2': OJDWR I O_CREAT, S~READ I S~WRITEt
if (nf2 = = -1)
perror(Herror al abrir el ficheroH
);
else
printftjichero abierto para leer y escribir  nH
);
Este programa utiliza la fund6n open (), para abrir un fichero llama
do datos1 para leer, y un fichero llamado datos2 para leer y escribir. Si d
fichero datos2 existe, se abre y si no se crea; en este ultimo caso, IeSOD
asignados los permisos especificados (lectura y escritura). Si el fichero exist~
los permisos son ignorados.
Esta fund6n abre un fichero especificado por path para escribir. Si elfi·
chero existe y tiene permiso de escritura, destruye su contenido. El argu·
mento pmodo, se aplica a ficheros de nueva creaci6n solamente. Su signifi-
cado es el mismo que se ha expuesto en la funci6n open( ).
# include <sys  types.h >
# include <sy  stat.h >
# include <io.h >
# include <errno.h >
La funci6n creat( ) retorna un numero que ha side asociado al fichero
creado, para dirigirse a el en posteriores operaciones de lectura 0 escritura.
Un valor -1 indica un error y la variable errno es puesta al valor correspon-
diente.
nf == creat(Hdatos': S~READ I S~WRITE);
if (nf == == -1)
perror(Herror al crear el jichero");
else
printj(Hjichero creado  n");
Esta funci6n indica si se ha llegado al final del fichero cuyo numero aso-
ciado es num.
#include < io.h >
# include <errno.h >
La fund6n eo/( ) devuelve el valor 1 si se ha llegado al final del fiche-
ro; en caso contrario, devuelve el valor O. Un valor -I indica un error y la
variableerrno es puesta al valor correspondiente.
/ * Leer hasta encontrar el fin de fichero */
while (!eof(nf))
[
/ * Leer 80 0 menos bytes */
if ((nbytes = read(nj, buffer, 80)) - - -1)
[
perror(HError de lectura");
break;
}
total + = nbytes; / * bytes lefdos */
}
printf(HNumero de bytes lefdos: %d n': total);
En este ejemplo, se Ieen datos del fichero referenciado por nf EI pro-
ceso finaliza cuando se a1canza el final del fichero 0 si ocurre un error du-
rante la lectura. EI resultado es el numero total de bytes leidos.
int nf;
nf = fileno(stdprn);
Este ejemplo asigna a nj, el numero asociado al fichero stdprn (im-
presora paralelo).
Esta funci6n cierra el fichero que tenga como descriptor num, liberando
as! dicho descriptor 10 que permitini que sea utilizado por otro fichero.
# include <io.h >
# include <errno.h >
La funci6n closer ) devuelve un 0 si el fichero es cerrado. Un valor
-1 indica un error y la variable errno es puesta al valor correspondiente.
Esta funci6n escribe c bytes, almacenados en buffer, en el fichero asociado
con num.
# include <io.h >
# include <errno.h >
La funci6n writer ) retorna el numero de bytes actualmente escritos.
Un valor -1 indica un error y la variable errno es puesta al valor correspon-
diente.
Esta funci6n intenta leer c bytes, del fichero cuyo descriptor asociado es
num, y los almacena en buffer.
# include <io.h >
# include <errno.h >
La funci6n read( ) retorna el numero de bytes actual mente leidos. Un
valor de 0 indica que se ha encontrado un fin de fichero. Un valor -1 indica
un error y la variable errno es puesta al valor correspondiente.
EI siguiente programa, recibe como panimetros el nombre de dos fi-
cheros y copia el contenido del primero sobre el segundo. Si el fichero des-
tino existe, se nos preguntani si queremos sobreescribirlo. Cuando el pro-
grama finaliza, presenta un mensaje para saber si la copia se ha hecho
satisfactoriamente, 0 si ha ocurrido un error.
/ * Funciones de bajo nivel (low-level junctions I/O).
* Reproducci6n de la orden copy.
* CCOPY.C
*/
# include <stdio.h >
# include <io.h >
# include <malloc.h >
# include <errno.h >
# include <conio.h >
# include <jcntl.h>
# include <sys  types.h >
# include <sys  stat.h >
int copiar-f(char *, char *);
/ * dejiniciones de constantes. S_ */
/ * prototipo */
main(int argc, char ~rgv[ J) / * junci6n principal */
[
if (argc = = 3)
if (copiar-f(argv[lJ, argv[2J))
printj(HFallo en la copia  n ");
else
printj(HCopia ejectuada  n");
else
printj(H FORMATO: CCOPY juente destino  n");
/ * Copiar un fichero en otro.. 81 tamano del buffer se asigna
* dindmicamente. Avisa si el fichero existe. Si la copia se
* efectua satisfactoriamente devuelve un 0, en caso contrario
*-devuelve un numero de error (errno).
*/
int copiar-f(char 4uente, char ~estino)
[
char *buf;
int n-fuente, n_destino, car;
unsigned bytes = 2048;
1* Abrir el fichero fuente y crear el fichero destino,
* sobreescribiendo si es necesario.
*1
if ((n-fuente = open(juente, O---.BINARY I O---.RDONLY)) = = - 1)
return errno;
n_destino = open(destino, O---.BINARY I O_WRONLY I O_CREAT I
O--.EXCL, S-lREAD I S-lWRITE);
if (errno = = EEXIST) 1* fichero existe *1
[
printj(HEI fichero destino existe. ;, Sobreescribir ? (sin) ");
car = tolower(getch( ));
if (car = = 's')
n_destino = open(destin0, OJINARY I O_WRONLY I O_CREAT I
O_TRUNC, S-.JREAD I S-.JWRITE);
printj( H  n" );
1
if (n_destino = = -1)
return errno;
/ * Asignacion dindmica de memoria para el buffer *1
if ((buf = (char *)malloc((size_t)bytes)) = = NULL)
return ENOMEM; 1* insuficiente memoria *1
/ * Leer del fichero fuente y escribir en el destino *1
while (!eof(n-fuente))
[
if ((bytes = read(n-fuente, buj, bytes)) = = -1)
return errno;
if ((bytes = write(n_destino, buj, bytes)) = = - 1)
J
/ * Cerrar jicheros y liberar memoria d
close(n-Juente);
close(n_destino );
jree(buj);
return 0;
Esta funci6n mueve el puntero de LIE asociado con el fichero abierto con
el numero num, a una nueva localizaci6n desplazada desp bytes de pos.
# include <io.h >
# include < stdio.h >
# include <errno.h >
La funci6n lseek( ) devuelve el desplazamiento en bytes de la nueva
posici6n relativa al principio del fichero. Un valor -IL indica un error)
la variable errno es puesta al valor correspondiente.
Esta funcion da como resultado la posicion actual del puntero de LIE, del
fichero asociado con num. Esta posicion es relativa al principio del fichero.
#include <io.h >
# include <errno.h >
La funcion teU( ) devuelve e1desplazamiento en bytes de la posicion
actual relativa al principio del fichero. Un valor -IL indica un error y la
variable errno es puesta al valor correspondiente.
EI siguiente programa, solicita de la entrada estandar, el nombre de
un fichero y un caracter. EI resultado que se busca es la posicion del pri-
mer caracter, existente dentro del fichero, igual al dado y e1numero total
de bytes de dicho fichero.
# include < stdio.h >
# include <stdlib.h>
# include <io.h >
# include <jcntl.h>
main( )
I
int num, carb;
unsigned bytes;
long desp, longit;
char car, nombre-J[31J;
do
(
printj(Hlntroducir el nombre del jichero: "),o
gets(nom bre-f),o
num = open(nombre-J, O-fiINARY I O-RDONLY),o
}
while (num = = -1);
/ * Buscar un cardcter especijicado */
lseek(num, OL, SEEK--..SET),o / *principio del jichero */
printj(HEscribir el cardcter a buscar: "),o
carb = getchar( ),o
/ * Leer hasta encontrar el cardcter buscado */
do
{
/ * bytes = bytes lefdos */
if ((bytes = read(num, &car, 1)) -1)
error(HError de lectura"),o
}
while ((car /= (char)carb) && bytes);
/ * Escribir el resultado obtenido */
desp = tell(num),o
if (bytes) / * si se ha encontrado */
printjt'  nEI primer ASCII %u t%c') estd en el byte %ld  n':
carb, carb, desp),o
else
printj(H  nEI ASCII %u t%c') no se encuentra  n': carb, carb);
/ * Longitud del jichero */
desp = lseek(num, OL, SEEK---.END),o
printj("  nEI jichero tiene un total de %d bytes  n': desp),o
close(num),o .
}
void error(char *mens_error)
(
perror(mens_error ),o
exit(l),o
}
Esta funci6n asocia un segundo descriptor, al fichero asociado con e1des-
criptor num.
# include <io.h >
# include <errno.h >
La funci6n dup( ) devuelve un nuevo descriptor si se ejecuta satisfac-
toriamente. Un valor -1 indica un error y la variable errno es puesta al va-
lor correspondiente.
Esta funci6n asocia un segundo descriptor num2, al fichero asociado con
el descriptor numl. Si ya existe un fichero abierto con el descriptor num2,
este es cerrado.
# include <io.h >
# include < errno.h >
La funci6n dup2() devuelve un 0 si se ejecuta satisfactoriamente. Un
valor-1 indica un error y la variable errno es puesta al valor correspondiente.
Esta funci6n abre el fichero especificado por path para el tipo de opera-
cionindicado por modo, y permite que sea compartido por otros procesos.
#include < fcntI.h >
# include < io.h >
# include <share.h >
# include <sys  types.h >
# include <sys  stat.h >
# include <errno.h >
O~PPEND, O_BINARY, O_CREAT, O-.-EXCL,
O_RDONLY O_RDWR, O_TEXT, O_TRUNC,
O_WRONLY
(Ver open( ))
SH_C OM PAT, SH_DENYNO, SH_DENYRD,
SH~ENYRW, SH_DENYWR
(Ver -Jsopen( ))
S_IWRITE, S_IREAD, S_IREAD I S_IWRITE
(Ver open( ))
La fundon sopen() retorna un numero (descriptor de fichero) que ha
sido asociado al fichero abierto, para dirigirse a el en posteriores operacio-
nes de lectura 0 escritura. Un valor -1 indica un error y la variable errno
es puesta al valor correspondiente.
Si SHARE.COM (0 SHARE.EXE) no esta instalado, MS-DOS igno-
ra el modo compartido.
Esta funcion pone la mascara de permisos del proceso actual, al modo es-
pecificado por pmodo. Esta mascara es utilizada para modificar los per-
misos asignados a los nuevos ficheros que se creen por medio de las fun-
dones creat(), open( ) y sopen(). Si en la mascara un bit esta a 1, el
correspondiente bit en el permiso asignado al fichero es puesto a O. Si es
0, no hay cambios.
# include <sys  types.h >
# include <sys  stat.h >
# include <io.h >
Bajo MS-DOS todos los ficheros tienen permiso de lectura; no es po-
sible, asignarles permiso de escritura solamente.
Por ejemplo, si el bit de escritura es puesto a 1 en la mascara, cual-
quier nuevo fichero creado seria de s610 lectura.
Este ejemplo pone el permiso de s610 lectura, a todos los futuros fi-
cheros que se creen por medio de las funciones creat( ), open ( ) y sopen( ).
FUNCIONES PARA LA CONSOLA Y
LOS PUERTOS DE E/S
Lasfunciones para la consola y los puertos de E/S, ejecutan las operacio-
nesdelectura y escritura sobre la consola (teclado/pantalla) 0 sobre un puer-
to especifico. La salida y la entrada de estas funciones pueden ser redirec-
cionadas. La redirecci6n se efectua a nivel de sistema operativo.
Estasfunciones son especificas de MS-DOS y OS/2. Las funciones utiliza-
daspara la entrada de datos, leen los mismos de la entrada estandar (stdin)
y las funciones utilizadas para la salida de datos, escriben los mismos so-
brela salida estandar (stdout). Las declaraciones para todas estas funcio-
nes, estan contenidas en el fichero conio.h.
Leeun caracter del teclado sin que se produzca eco (el caracter no se vi-
sualizaen pantalla). No es necesario pulsar Enter. No hay conversi6n de
caracteres: CR+LF no es convertido a f n:
La funcion getch( ) devuelve el canicter leido. Si este valor es cera, !Ia·
mar otra vez a la funcion para recuperar el segundo codigo (ver Codigo
Extendidos en los apendices).
Lee un canicter del teclado, produciendose eco (el caracter se visualiza en
pantalla). No es necesario pulsar Enter. No hay conversion de caracteres:
CR + LF no es convertido a '  n:
La funcion getche( ) devuelve el caracter leido. Si este valor es cero,
llamar otra vez a la funcion para recuperar el segundo codigo (ver Codigos
Extendidos en los apendices).
Escribe directamente en la consola, el caracter c. No hay conversion de ca·
racteres: '  n' no es convertido a CR + LF.
La funcion putch( ) retorna el caracter c si se ejecuta satisfactoriamente
y un valor EOF si ocurre un error.
Esta funcion chequea el teclado, buscando el canicter correspondiente a
la tecla mas recientemente pulsada.
La funcion kbhit( ) devuelve un valor distinto de 0 si se ha puisado
una tecla, si no devuelve un valor O.
printf(HPulse una tecla para continuar ");
while (!kbhit( ));
Esta funcion devuelve a la memoria intermedia de la consola, el ultimo
canicter leido, haciendo que este caracter sea el proximo caracter a leer.
La funcion ungetch( ) devuelve el caracter car si existe. Si el valor de-
vuelto es EOp, entonces ha ocurrido un error.
# include <stdio.h>
# include < conio.h >
# include <ctype.h >
main( )
{
char car;
iot n;
putst'Datos ...");
car = getche( );
while (lisdigit(car))
car = getche( );
car = ungetch(car);
cscanf("%d': &n);
printf(" n%d n': n*lOO);
}
Datos ...
boligrafos
25
Este ejemplo lee caracteres hasta leer un digito. EI digito leido esde·
vuelto al buffer por la funci6n ungetch( ), para que a continuaci6n la fun·
ci6n cscanf( ) lea el valor tecleado.
Lee directamente de la consola una cadena de caracteres, hasta encontrar
el canicter '  n', y almacena en buffer la cadena y su longitud, de la forma
siguiente:
buffer[O] contiene el maximo numero de caracteres que pueden leerse,in·
cluyendo el canicter nulo de terminaci6n.
buffer[l] contiene el numero de caracteres leidos, excluyendo el canicter
nulo de terminaci6n.
La funci6n cgets( ) retorna un puntero al comienzo de la cadena de
caracteres (direcci6n de bujjer[2]).
la funcion cputs( ) retorna un 0 si se ejecuta satisfactoriamente y un valor
distinto de 0 si ocurre un error.
Leedatos directamente de la consola, los interpreta de acuerdo con el for-
mato indicado y los almacena en los argumentos especificados. No hay con-
version de caracteres: CR + LF no es convertido a '  n'. Cuando se leen
varios datos consecutivos, no hay que poner especial cuidado en eliminar
el canicter LF, que normalmente queda en el buffer del teclado.
La funcion cscanf( ) retorna un entero, correspondiente al numero de
datos leidos y asignados, 0 un EOP cuando se intenta leer un end-of-file.
Escribedirectamente en la consola datos con formato. No hay conversion
de caracteres: ' n' no es convertido a CR + LF.
# include <conio.h >
# define N 20
int main(void)
{
char buffer[NJ
int n;
printf(<tlntroducir una cadena (%d caracteres max): ': N-l);
cadena = cgets(buffer);
printj(<t  nMdximo nOde caracteres: %d  n': buffer[O]);
printj(<tN° de caracteres actuales: %d  n': buffer[l]);
cputs( cadena);
putch(  n'); putch(  r');
cprintj(<tlntroducir el nOde elementos: ");
cscanf(<t%d': &n);
cprintj(<t  nTipo de elementos: ");
gets(cadena);
cprintj(<t  nHay %d %s  n  r': n, cadena);
Observar la utilizacion del canicter '  r' debido a que no hay conver·
sion ('  n' a CR +LF). Observar tambien, que despues de cscanf( ) no es
necesario limpiar el buffer del teclado; al no quedar en el mismo el canic·
ter LF, gets( ) se ha ejecutado normalmente.
Con respecto al teclado, sabemos que al pulsar una tecla se genera un byte,
correspondiente al codigo ASCII del canicter (ver codigo de caracteres de
ASCII, en los apendices).
char byte;
byte = getch( );
printf("%d': byte);
Este ejemplo da como resultado el valor ASCII de la tecla pulsada.
Par ejemplo, si pulsamos la tecla 'a', se escribe 97.
Sin embargo, existen muchas teclas y combinaciones de teclas no re-
presentadas por un unico byte. Por ejemplo las teclas FI a Fl6 0 la combi-
naci6n de teclas AU+A (ver c6digos extendidos en los apendices). El c6di-
go de estas consta de dos bytes. El primer byte siempre vale O.
char byte], byte2;
byte] = getch( ); byte2 = getch( );
printf("%d O/Od': byte], byte2);
Este ejemplo da como resultado el c6digo extendido correspondiente
a estetipo de teclas. Por ejemplo si pulsamos FI, se escribe 0 59, si puIsa-
mas AU+A se escribe 0 30. Observar que tenemos que emplear dos veces
la funci6n getch( ).
El sistema operativo MS-DOS dispone de un sistema para controlar
directamente el cursor sobre la pantalla. Este sistema no esta implementa-
do sabre la memoria ROM como las rutinas norm ales de acceso a disposi-
tivos de E/S, sino que esta contenirn un fichero separado llamado
ANSI.SYS.
Para poder utilizar este fichero, debe ser previamente instalado en el
sistemaoperativo. Para ello incluir en el fichero del sistema CONFIG.SYS
la orden: DEVICE = ANSI.SYS.
ANSI.SYS, para controlar el cursor, interpreta secuencias de escape
seguidasde uno 0 mas caracteres especiales.
Los caracteres especiales que podemos poner a continuaci6n, son las
siguientes:
2J
K
A
B
C
D
010d; 010df
s
u
O1odA
010dB
O1odC
O1odD
O1odm
O1od;010dm
Borra la pantalla.
Borra la linea donde esta el cursor.
Mueve el cursor una fila hacia arriba.
Mueve el cursor una fila hacia abajo.
Mueve el cursor una columna a la derecha.
Mueve el cursor una columna a la izquierda.
Mueve el cursor a la fila y columna especificadas.
Memoriza la posici6n del cursor.
Restaura la posici6n.
Mueve el cursor O1od filas hacia arriba.
Mueve el cursor O1od filas hacia abajo.
Mueve el cursor O1od columnas hacia la derecha.
Mueve el cursor O1od columnas hacia la izquierda.
Cambiar el atributo de un caracter.
Color del primer plano y color de fondo.
Cada caracter visualizado sobvera pantalla, es almacenado en memo-
ria con dos bytes. Un byte contiene el c6digo normal del caracter y el otra
contiene el atributo del caracter.
Volver a modo normal (blanco sobre negro).
Caracteres en alta intensidad.
Caracteres subrayados.
Caracteres parpadeando.
Caracteres en video inverso.
Caracteres no visibles.
Los colores para el primer plano y para el fonda se pueden elegir de
entre los presentados en la tabla siguiente:
Negro
Rojo
Verde
Amarillo
Azul
Magenta
Cyan
Blanco
Estas secuencias de caracteres pueden ser transmitidas por la funci6n
printf( }.
printj("  x1B[2J"}; / * borra la pantalla d
/ * situar el cursor en la fila 10, columna 1 */
printj(',  x1B[%d;%dj': 10, 1);
printf("  x1B[7m "}; ) / * escribir en video invertido */
printf("  x1B[Om"}; / * vuelta a normal */
/ * Presentaci6n en pantalla, en verde con fondo azul */
printf(" x1B[%d;%dm': 32, 44);
printf("  x1B[Om"}; / * vuelta a normal */
El siguiente ejemplo muestra un programa que presenta un menu en
pantalla. Cada opci6n de este menu se elige situandonos sobre ella con las
teclasde movimiento del cursor (arriba, abajo). La opci6n elegida siempre
esta en video inverso. Para ejecutarla se pulsa la tecla Enter.
# include < stdio.h >
# include < stdlib.h >
# dejine VERDAD 1
#dejine NUM 7
# dejine BPAN " x1B[2J"
#dejine BLIN "x1B[K"
#dejine INVER"  x1B[7m"
#dejine NORMAL "x1B[Om"
#dejine LIN_U 72
#dejine LIN----.D 80
#dejine ENTER 13
/ * numero de lfneas del menu */
/ * borrar pantalla */
/ * borrar lfnea */
/ * vfdeo inverso */
/ * vuelta a normal */
/ * cursor una lfnea hacia arriba */
/ * cursor una lfnea hacia abajo d
/ * tecla Enter */
/ * Cursor a la posicion x, y */
#dejine POS_C(x, y) printjt'x1B[%d;%dj': (x), (y))
main() 
{
void menu(char *[J, int, int);
int getcodigo(void);
void ejecutar(int);
static char *linea[NUMJ
{
printf(BPAN);
while (VERDAD)
{
menu(linea, NUM, pos_cursor);
codigo = getcodigo( );
switch (codigo)
{
case LIN_U
"Altas':
"Bajas':
"Modijicaciones':
"Visualizacion de un registro':
"Listado por pantalla':
"Listado por impresora':
"Salir"
};
int pos_cursor = 0;
int codigo;
/ * cursor en la prim era lfnea del menu */
/ * codigo de la tecla pulsada *1
/ * visualizar menu *,
/ * explorar teclado *,
if (pos_cursor > 0) --pos_cursor;
else pos_cursor = NUM-1;
break;
case LIN--.D:
if (pos_cursor < NUM-1) + +pos_cursor;
else pos_cursor = 0;
break;
case ENTER:
ejecutar(pos_cursor );
break;
1
1
1
/*******************'(unci6n visualizar menu *******************/
void menu(char *linea[J, int num, int pos)
[
int f;
for if = 0;f < num; f + +)
[
POS_C(10+ f, 25);
if if = = pos)
printj(H%s%s%s  n': INVER, *(linea+f), NORMAL);
else
printj(H%s  n': *(linea+f));
/******************* Funci6n explorar teclado *******************
int getcodigo(void)
[
int tecla;
while ((tecla = getch( )) /= 0)
if (tecla = = 13) return(tecla);
return(getch( ));
1
/ * esperar por un 0 inicial */
/ * se ha pulsado Enter */
/ * devolver segundo c6digo */
1******************* Funci6n ejecutar opci6n *******************/
void ejecutar(int pos)
(
printj(BLIN);
switch(pos)
(
case 0:
printj(<tAltas"); break;
case 1:
printj(<tBajas"); break;
case 2:
printjt(Modijicaciones"); break;
case 3:
printf(<tVisualizaci6n de un registro"); break;
case 4:
printjt(Listado por pantalla"); break;
case 5: 
printj(<tListado por impresora"); break;
case 6:
exit(O);
}
}
I * Las Ifneas printj de esta sentencia switch, estdn sustituyendo
* a 10 que en la realidad sedan llamadas a rutinas
*1
Un puerto de E/S es una zona fisica utilizada como medio de comunica-
ci6n entre la unidad central de proceso y un dispositivo periferico. Los puer·
tos se identifican por direcciones especiales de E/S de 16 bits. Para trans-
mitir un dato, primero se envia su direcci6n de destino (puerto) por el bus
de direcciones, y a continuaci6n se envia el dato por el bus de datos. Estas
direcciones no tienen nada que ver con las direcciones de la memoria RAM.
Su utilizaci6n se requiere normalmente, cuando los servicios de interrup·
ciones del BIOS (Basic Input/Output System) y del DOS (Disk Operating
System) no proveen soporte para un dispositivo periferico; por ejemplo,
si queremos enviar un dato al altavoz, tenemos que hacerlo a traves desu
puerto de E/S. El BIOS provee el acceso a los dispositivos perifericos es-
tandar tales como el teclado, pantalla, unidad de disco flexible, unidad de
disco duro, impresora, reloj, y joystick; mientras que el DOS provee servi-
cios adicionales que suplementan al BIOS.
Cada dispositivo periferico de un ordenador esta asignado a un rango
de direcciones de memoria de E/S las cuales pueden variar de unos orde-
nadores a otros. Como ejemplo, citamos a continuacion algunos puertos
de uso comun en ordenadores personales.
Ox040-0x05F
Ox060-0x06F
OxlFO-OxlF8
Ox3BC-Ox3BF
Ox3DO-Ox3Df
Ox3FO-Ox3F7
Ox3F8-0x3FF
Timer (8254.2)
Teclado (8042)
Controlador de disco duro
Puertotaralelo 1 (impresora paralelo)
Tarjeta"GA 0 MCGA 0 VGA emulando CGA
Controlador de disco flexible
Puerto serie 1
Esta funcion lee un byte del puerto de entrada especificado. EI valor del
argumento puede ser cualquier entero sin signo, en el rango 0 a 65535 (OxOOOO
a OxFFFF).
Esta funcion escribe un byte en el puerto de salida especificado. EI valor
de puerto puede ser cualquier entero sin signo, en el rango 0 a 65535; byte
puede ser cualquier entero, en el rango de 0 a 255.
Esta funci6n lee una palabra de dos bytes del puerto de entrada especifica·
do. EI valor del argumento puede ser cualquier entero sin signa, en elran·
go 0 a 65535.
Esta funci6n escribe una palabra de dos bytes en el puerto de salida esp
ficado. EI valor de puerto puede ser cualquier entero sin signo, en el rang
o a 65535; palabra puede ser cualquier entero, en el rango de 0 a 65535
EI siguiente programa emite por el altavoz un pitido de una deter
nada frecuencia y duraci6n.
EI metoda consiste en utilizar el temporizador programable inte
del PC (timer) para enviar al altavoz una frecuencia determinada. Elti
(8254) obtiene una sefial proporcionada por el reloj principal del PC, q
oscila a una frecuencia de 1193180Hz. Para producir un sonido con elte
porizador primero, hay que programar el temporizador para generar
frecuencia; a continuaci6n, se debe de dirigir la salida del temporiza
al altavoz. El sonido se producini indefinidamente, a menos que se desac-
tive el altavoz.
EI temporizador se programa proporciomindole un numero. Cuando
recibe una orden de dirigir su salida al altavoz, cuenta pulsos de reloj del
sistema (oscila a 1193180Hz) hasta alcanzar el valor proporcionado. En:-
tonces se produce un pulse y comienza la cuenta otra vez desde cero. De
acuerdo con esto, para un sonido de frecuencia de valor 987 Hz, progra-
maremos e1temporizador con un valor: c = 1193180/987.
1* Emitir un sonido por el altavoz. Utilizar los puertos
* 66, 67 y 97 para control del timer y del altavoz.
*1
# include <stdio.h>
# include <conio.h >
main( )
[
int a, c, lb, hb;
/ long duracion = 100000;
const int frec = 987;
c = 11931801 frec;
lb = c & OxFF;
hb = c > > 8;
outp(Ox43, OxB6);
outp(Ox42,lb);
outp(Ox42,hb);
a = inp(Ox61);
outp(Ox61,a I Ox3);
while (--duracion > 0);
outp(Ox61,a);
]
1* frecuencia sonido *1
1*frecuencia timer entre
frecuencia sonido */
/ * byte de menor peso *1
/ * byte de mayor peso *1
/ * indicar al timer env{o de datos */
/ * enviar el byte de menor peso */
1* enviar el byte de mayor peso */
/ * salvar byte de control altavoz */
/ * activar el altavoz */
/ * duraci6n */
/ * desactivar el altavoz */
3
Utilizando el Preprocesador
• El Preprocesador de C
INTRODUCCION
/
EI preprocesador de Microsoft C soporta todas las directrices definidas en
ANSI C, las cuales permiten sustitucion de macros, compilacion condicio-
nal e inclusion de ficheros fuente; y ademas aporta otras para el control
de la numeracion de lineas en diagnosticos y en la depuracion, para gene-
rar mensajes y para ejecutar acciones especificas del compilador.
Las directrices para el preprocesador son utilizadas para hacer pro-
gramas fuente faciles de cambiar y de compilar en diferentes situaciones.
Una directriz comienza con el simbolo # como primer caracter, distinto
de blanco, en una linea, e indica al preprocesador una accion especifica
a ejecutar. Pueden aparecer en cualquier parte del fichero fuente, pero so-
lamente se aplican desde su punta de definicion hasta el final del progra-
ma fuente.
La declaracion de una directriz puede ser continuada en una linea si-
guiente, colocando el caracter  inmediatamente antes del caracter nueva
linea (NL).
Cuando se ejecuta la orden para compilar un fichero fuente, el pre-
procesador procesa el texto fuente, como primer paso, y antes de que el
compilador analice dicho fichero. Este proceso consiste en:
#define identificador texto
# define identificador(parametros) texto
1. Si es necesario, se introducen caracteres NL para reemplazar 105
indicadores de fin de linea dependientes del sistema.
2. La secuencia  NL es borrada y la linea siguiente es afiadida a
la linea que contenia la s'ecuencia.
3. El texto fuente es descompuesto en tokens (elementos reconoci-
dos por C) con el fin de localizar las llamadas a las macros. Cada
comentario es reemplazado per un espacio en blanco. Una linea
cuyo primer canicter es # es tratada como una orden por el pre-
procesador.
4. Se ejecutan las directrices, se expanden las macros, las secuencias
de escape son reemplazadas por sus equivalentes y las cadenas,de
caracteres adyacentes son enlazadas.
~
DIRECTRIZ # define. Sustituci6n de srmbolos
Esta directriz es utilizada para asociar identificadores con palabras clave,
constantes, sentencias y expresiones. Cuando un identificador representa
sentencias 0 expresiones se denomina macro.
La directriz # define sustituye todas las apariciones identificador 0 iden·
tificador(panimetros) en el fichero fuente por texto.
Para mayor claridad del programa, las constantes simb61icas suelen
expresarse en mayu.sculas con el fin de distinguirlas de las otras variables.
Panimetros, representa una lista de parametros formales entre paren-
tesis y separados por comas, que seran reemplazados por sus correspon-
dientes parametres actuales en la sustituci6n. Entre el identificador yel
parentesis abierto no puede haber un espacio en blanco, para no confundir
los parametres con el texto.
# define ANCHO 70
# define LONGITUD (ANCHO + 10)
Este ejemplo define un identificador ANCHO como la con stante 70
y define LONGITUD como ANCHO + 10, esto es, 70 + 10. Cada ocu-
rrencia de ANCHO en el fichero fuente es sustitufda por 70, y cada ocu-
rrencia de LONGITUD por (70 + 10). Los parentesis son importantes, mas
bien necesarios, para obtener los resultados esperados. Por ejemplo:
En el caso de no haber utilizado parentesis en la definicion de LON-
GITUD,el resultado serfa:
Este ejemplo define una macro denominada MENOR. Por ejemplo,
una ocurrencia en el programa fuente como:
Este operador es utilizado solamente con macros que reciben argumentos.
Este operador precediendo al nombre de un panimetro formal en la ma-
cro, hace que el correspondiente panimetro actual pasado en la Hamada
a la macro, sea inc1uido entre comillas para ser tratado como un literal.
printf("Pulse una tecla para continuar" " n "); equivalente a:
printf("Pulse una tecla para continuar  n");
Este operador al igual que el anterior, tambien es utilizado con macros que
reciben argumentos. Este operador permite la concatenaci6n de dos cadenas.
printf("elementa " "1" " = %d  n': elemental);
printj("elementa 1 = %d  n': elemental);
Cuando se utiliza este formato, se sustituye esta linea por el contenido
del fichero especificado. El fichero se busca en primer lugar en el directo-
rio actual de trabajo y posteriormente en los directorios estandar definidos.
Si se utiliza este otro formato, el fichero solamente es buscado en los
directorios estandar definidos.
Las siguientes directrices permiten compilar 0 no partes seleccionadas del
fichero fuente. La sintaxis es la siguiente:
# if expresion
[grupo-de-ltneas;]
[ # elif expresion 1
grupo-de-Hneas;]
[ # elif expresion 2
grupo-de-Hneas;]
[ # elif expresion N
grupo-de-Hneas;]
[# else
grupo-de-Hneas;]
#endif
donde grupo-de-Hneas representa cualquier numero de lineas de texto de
cualquier tipo.
EI preprocesador selecciona un unico grupo-de-ltneas para pasarlo al
compilador. EI grupo-de-ltneas seleccionado sera aquel que se correspon-
da con un valor verdadero de la expresi6n que sigue a # if 0 # elif. Si todas
las expresiones son falsas, entonces se ejecutara el grupo-de-ltneas a conti-
nuaci6n de # else.
# define EEUU ]
#define ESPANA 2
# define FRANCIA 3
# if ESTADO---.ACTIVO = = EEUU
char moneda[ ] = "dotar ";
#elif ESTADO---.ACTIVO = = ESPANA
char moneda[ ] = "peseta";
#elif ESTADO---.ACTIVO = = FRANCIA
char moneda[ ] = "franco";
#endif
main( )
(
Cada directriz # if en un fichero fuente debe emparejarse con su co-
rrespondiente # endif.
Una expresi6n puede contener el operador del preprocesador C, defi-
ned, cuya sintaxis es:
Este operador retorna un valor verdadero si el identificador esta ac-
tualmente definido y retorna un valor falso, en caso contrario.
# define REG] register
# define REG2 register
# if defined(M_86)
# define REG3
# define REG4
# define REG5
# else
# define REG3 register
# if defined(M_68000)
# define REG4 register
# define REG5 register
# else
# define REG4 register
# define REG5
#endif
#endif
func(a)
REG3 jnt a;
I
REGl jnt b;
REG2 jnt c;
REG5 jnt d;
En este ejemplo, las directrices # if y # endif controlan las declaracio-
nes register en un fichero portable. Cuando se define ~86, el preproce-
sador borra el identificador REG3 y REG5 del fichero reemplazandolo POI
un texto nulo, 10 cual hace que a y d no puedan ser definidas como varia-
bles de esa c1ase, mientras que silo son b y c. Cuando se define ~68000,
las cuatro variables a, b, c y d son declaradas de c1ase register. Cuando no
se definen ni M_86 ni M_68000, solamente a, by c son declaradas de
c1ase register.
# ifdef identificador
# ifndef identificador
# ifdej comprueba si el identificador esta definido e # ifndej comprueba
si el identificador no esta definido.
La linea # ifdej id es equivalente a # if dejined(id), y la linea # ifndej
id es equivalente a # if !dejined(id).
Estas directrices simplemente garantizan la compatibilidad con ver-
sionesanteriores de C, ya que su funci6n es ejecutada perfectamente por
el operador dejined(identificador).
Estadirectriz va seguida de un numero entero y opcionalmente de un iden-
tificador.
Una linea de la forma indicada pone las constantes predefinidas
---LINE __ y ----.FILE __ a los valores indicados por cte-entera e
identificador respectivamente, 10 cual hace que el compilador cambie su
contador interne y su nombre de fichero de trabajo, por los valores especi-
ficadosen estas constantes. Si se omite el nombre de fichero, se utiliza el
que tenga la constante ----.FILE __ por defecto.
La informaci6n proporcionada por hi directriz # line se utiliza sim-
plementecon el objeto de dar mensajes de error mas informativos. Cuan-
do se ejecuta esta directriz, la siguiente linea de texto a tomar es la indica-
da por la constante entera especificada y del fichero especificado. EI
compilador utiliza este numero de linea y fichero de informaci6n para dar
los avisos y mensajes de error.
Esta directriz es utilizada para abortar una compilaci6n, sacando el men-
saje de error especificado a continuaci6n de la misma.
Esta directriz tiene utili dad cuando en un programa incluimos un pro-
ceso de compilaci6n condicional. Si se detecta una condici6n anormal, po-
demos abortar la compilaci6n utilizando esta directriz, al mismo tiempo
que se visualiza el mensaje de error especificado.
# if !defined(MSDOS)
# error MSDOS no dejinido: ver las opciones Iu 0 IV
#endif
Instruye al compilador para ejecutar una acci6n particular en tiempo de
compilaci6n.
Las opciones del compilador tienen efecto durante toda la compila·
ci6n. Si deseamos que esto no suceda asi, podemos anular el efecto de la
opci6n especificada, 0 por defecto, utilizando la directriz # pragma. A con-
tinuaci6n exponemos algunas de ellas.
Instruye al compilador para activar (on) 0 desactivar (off) el chequeo de
punteros; esto es, comprobar si hay punteros nulos y punteros fuera de rango.
Requiere la utilizaci6n de la opci6n IZr. Esta opci6n sera utilizada en la
orden CL, junto con la opci6n Iqc (CL Iqc IZr progxx.c). La opdon por
defecto depende de la opci6n correspondiente del compilador.
Instruye al compilador para activar (on) 0 desactivar (off) el chequeo de
la pila; esto es, comprobar si el tamafio de la pila es excedido. Por defecto,
el chequeo de la pila esta activo (ver opcion ICe; opcion por defecto).
La forma de proceder para desactivar el chequeo de la pila para una
determinada funcion, asumiendo que dicho chequeo esta activo (opcion
IOe), es la siguiente:
#pragma check----.Stack( off)
static void func(int argl, char *arg2)
[
J
#pragma check----.Stack( on)
Instruye al compilador para que compile las funciones especificadas como
funciones intrinsecas. Ver tambien la opcion IOi.
Instruye al compilador para que compile las funciones especificadas como
funciones estandar.
Algunas funciones de la libreria estandar de C, est an implementadas de
dos formas: como funciones estandar y como funciones intrinsecas. La fun-
cion en su forma estandar, cuando se llama, utiliza el stack. La funcion
en su forma intrinseca no necesita de una llamada para ser ejecutada; se
genera como una funcion en linea (su codigo es expandido en la llamada).
Los programas que utilizan funciones intrinsecas son mas rapidos; aunque
pueden ser mas largos debido al codigo adicional generado. Las funciones
de Microsoft C que podemos tratar como funciones intrinsecas son las si-
guientes:
abs( ), acos( ), acosl( ), asin( ), asinl( ), atan( ), atanl( ),
atan2( ), atan21( ), cei/( ), ceil/( ), cos( ), cosl( ), cosh( ),
coshl( ), exp( ), expl( ), jabs( ), jloor( ), jloorl( ), jmod( ),
jmodl( ), labs( ), log( ), logl( ), loglO( ), loglOI( ), pow( ),
powl( ), sin( ), sinl( ), sinh( ), sinhl( ), sqrt( ), sqrtl( ),
tan( ), tanl( ), tanh( ), tanhl( )
Si utilizamos la opci6n del compilador /Oi, todas la funciones que
aparezcan en el programa pertenecientes a este conjunto, senin tratadas como
intrinsecas. Cuando deseemos tratar como intrinsecas s610 determinadas
funciones, utilizaremos la directriz #pragma intrinsic(funcl[, junc2} ...}.
La directriz # include afiade el contenido de un fichero dado, .h, en otro
fichero. Los ficheros a incluir pueden ser utilizados para incorporar defi-
niciones de constantes, macros, declaraciones de variables extemas y tipos
complejos de datos, a cualquier fichero fuente.
/ ************************ PROGl102.C ************************
MODULO PRINCIPAL
/ * La siguiente linea incluye el jichero especijicado */
# include "inclllOJ.h"
# include "stdio.h"
main( ) / * FUNCION PRINCIPAL */
[
LeerRegistro( );
Verificar();
putchar(  n');
puts(mensaje);
]
/ ************************ PROGl103.C ************************
MODULO PARA CONTENER LOS PROCEDIMIENTOS
/ * La siguiente linea incluye el jichero especijicado */
# include "incll102.h"
# include "string.h"
# include "stdlib.h"
# include "stdio.h"
void LeerRegistro( )
[
system("cls ");
printf("Denominacion
printj(' 'Existencias
]
"); gets(registro.denominacion);
"); scanf("%d': &registro.existencias);
void Verificar()
{
if (registro.existencias < 5)
strcpy(mensaje, "Por debajo de mfnimos");
else
strcpy(mensaje, "Por encima de mfnimos");
#if !defined(~NCLllOl~)
# define ~NCLllOl~ 1
void LeerRegistro(void);
void Verificar(void);
#if !defined(~NCLl102~)
# define ~NCLl102~ 1
struct TipoReg
{
char denominacion[30];
int existencias;
};
struct TipoReg registro;
char mensaje[25];
Este programa consta de cuatro m6dulos salvados en cuatro ficheros
diferentes. Los ficheros .h seran incluidos por el preprocesador de C, cuando
emitamos la orden para compilar.
Este apartado presenta una utilidad sencilla que nos permitira medir el tiem-
po de ejecuci6n de cualquier parte de nuestro programa C.
En primer lugar crearemos un fichero "tiempo.h" que contendra las
macros T_INICIAL(descripcion) y T~INAL. Cuando un programa fuen-
te incluya estas macros, s6lo generara el c6digo correspondiente a ellas,
si la orden de compilaci6n del programa define la macro TIEMPO
eeL IDTIEMPO progxx.c) y se ha especificado el fichero de cabecera "tiem-
po.h"; en otro caso, las referencias a estas macros seran nulas.
La opci6n IDid[ = [valor]] define la constante simb6lica id para el pre-
procesador. Si valor no se especifica, el valor de id es 1.
/ * cctiempo.h" Contiene las macros: T~NICIAL(DESCRIPCION)
* T-FINAL
*/
# if !defined(TIEMPO~EFINIDO)
#if defined(TIEMPO)
# include <stdio.h>
# include <time.h >
clock_t inicial, final;
#define T~NICIAL(DESCRIPCION) 
printf(CC  n  npara : %s': # descripcion); 
inicial = clock( );
#define T-FINAL final = clock( ); 
printf(CC ntiempo : %g seg': 
(do" ble)(fin ai-in icial) / (do" ble)CLOCKS~ ER_SEC);
#define TIEMPO~EFINIDO
#else
# define T~NICIAL(DESCRIPCION)
# define T-FINAL
#endif
#endif
Observar el caracter de continuaci6n , en cada una de las lineas.
No se pueden introducir comentarios dentro de la definici6n de una macro.
Notar que el argumento descripci6n de la macro T~NICIAL no ne-
cesita especificarse entre comillas, ya que utilizamos el operador #.
T~NICIAL(lazo con variable unsigned int register);
for (i = 0; i < 65535; i+ +);
T---.FINAL;
EI programa correspondiente a la descripci6n realizada, se expone a
continuaci6n.
int main(void)
(
register unsigned int i;
unsigned int k;
T~NICIAL(lazo con variable unsigned int);
for (k = 0; k < 65535; k+ +);
T---.FINAL;
para : lazo con variable unsigned int register
tiempo : 0.06 seg
para : lazo con variable unsigned int
tiempo : 0.16 seg
# include Htiempo.h"
# include <math.h >
int main(void)
[
register int i = 0;
float jvalor = 10.0;
double dvalor = 10.0;
long double ldvalor = 10.0;
T~NICIAL(fmod( ) con argumentos jloat);
for (i = 0; i < 32000; i+ +)
fvalor + = jmod(fvalor, 2.0);
LYINAL;
T~NICIAL(fmod( ) con argumentos double);
for (i = 0; i < 32000; i+ +)
dvalor + = fmod(dvalor, 2.0);
L ...FINAL;
T~NICIAL(fmod( ) con argumentos long double);
for (i = 0; i < 32000; i+ +)
ldvalor + = jmod(ldvalor, 2.0);
L...FINAL;
)
para : jmod( ) con argumentos jloat
tiempo : 17.8 seg
para : jmod( ) con argumentos double
tiempo : 18.12 seg
para : jmod( ) con argumentos long double
tiempo : 18.68 seg
para : jmod( ) con argumentos jloat
tiempo : 24.39 seg
para : jmod( ) con argumentos double
tiempo : 21.42 seg
para : jmod( ) con argumentos long double
tiempo : 25.32 seg
El preprocesador sustituye cada llamada a una macro por su definicion;
dicho de otra forma, expande la macro. Las funciones no son expandidas;
cuando se llama a una funci6n se utiliza la pila (stack) para almacenarla
direcci6n de retorno y 10s argumentos pasados.
# include Htiempo.h"
# include <math.h >
#pragma intrinsic(floor)
main( )
(
double dvalorl
i.nt i = 0;
T~NICIAL(m6dulo (%) directamente);
for (i = 0; i < 32000; i+ +)
resto = dvalorl-jloor(dvalorl/dvalor2) *Clvalor2;
T---.FINAL;
T~NICIAL(m6dulo (%) empleando una macro);
for (i = 0; i < 32000; i+ +)
resto = MACRO~OD(dvalorl, dvalor2);
T---.FINAL;
T~NICIAL(m6dulo (%) empleando una junci6n);
for (i = 0; i < 32000; i+ +)
resto = junc_mod(dvalorl, dvalor2);
T---.FINAL;
I
double junc_mod(double dvl, double dv2)
(
return(dvl-jloor(dvl/dv2) *Clv2);
I
para : m6dulo (010) directamente
tiempo : 25.32 seg
para : m6dulo (%) empleando una macro
tiempo : 25.38 seg
para : m6dulo (%) empleando una funci6n
tiempo : 25.81 seg
4
structuras Dinamicas y Algoritmos
Estructuras Dinamicas y Algoritmos
Algoritmos Recursivos de Ordenaci6n y de Busqueda
La propiedad caracteristica de las estructuras dimimicas es la facultad que
tienen para variar su tamafio y hay muchos problemas que requieren de
estetipo de estructuras. Esta propiedad las distingue claramente de las es-
tructuras estaticas fundamentales (arrays y estructuras). Por tanto, no es
posible asignar una cantidad fija de memoria para una estructura dimimi-
ca, y como consecuencia un compilador no puede asociar direcciones ex-
plicitas con las componentes de tales estructuras. La tecnica que se utiliza
mas frecuentemente para resolver este problema consiste en realizar una
asignacion dinamica de memoria; es decir, asignaci6n de memoria para las
componentes individuales, al tiempo que son creadas durante la ejecuci6n
del programa, en vez de hacer la asignaci6n durante la compilaci6n del
mismo.
Cuando se trabaja con estructuras dinamicas, el compilador asigna
una cantidad fija de memoria para mantener la direccion del componente
asignado dimimicamente, en vez de hacer una asignaci6n para el compo-
nente en si. Esto implica que debe haber una clara distinci6n entre datos
y referencias a datos y que consecuentemente se deben emplear tipos de
datos cuyos valores sean punteros 0 referencias a otros datos.
Cuando se asigna memoria dimimicamente para un objeto de un tipo cual-
quiera, se devue1veun puntero a la zona de memoria asignada. Para reali-
zar esta operaci6n disponemos en C de la funci6n mal/oc(t).
Esta funci6n asigna un bloque de memoria de t bytes y devuelveun
puntero que referencia el espacio asignado. Si hay insuficiente espacio de
memoria 0 si t es 0, la funci6n retorna un puntero nulo (NULL).
Puesto que el puntero devuelto es a un objeto de un tipo no especifi-
cado (void), utilizar la notaci6n cast sobre el valor devuelto, para realizar
la conversi6n al tipo deseado.
typedef struct datos elemento;
struct datos
[
/ * miembros de la estructura */
elemento *NuevoElemento(void)
[
return(( elemento *)mal/oc(sizeof(elemento )));
}
main( )
[
elemento *c;
c = NuevoElemento( ); / * asignaci6n dindmica de memoria */
if (Ic)
[
printj(C(error: insuficiente espacio de memoria  n");
exit(l);
}
free(c); / * liberar el bloque de memoria apuntado por c */
}
La funci6n NuevoElemento( ) asigna memoria para un objeto de tipo
elemento y devuelve un puntero a dicho objeto. Si no hay espacio suficien-
te para crear un objeto del tipo especificado, la funci6n NuevoElemento( )
devuelve un puntero nulo (0 0 NULL).
En programas que utilizan asignaci6n dimimica de memoria, es muy
importante liberar este espacio cuando no se utilice, ya que si se pierde la
direcci6n que referencia el espacio asignado, dicho espacio no puede ser
liberado y ademas permanece inaccesible.
Para liberar un bloque de memoria asignado por la funci6n mal/ocr ),
utilizaremos la funci6n freer ).
Un array dinamico, segun se ha definido en el capitulo de punteros, una
vez creado no permite alterar su tamafio. Si 10 que deseamos es una lista
de elementos u objetos de cualquier tipo, originalmente vacia, que durante
la ejecuci6n del programa vaya creciendo y decreciendo elemento a elemento,
segunlas necesidades previstas en el programa, entonces tenemos que cons-
truir una lista lineal en la que cada elemento apunte 0 direccione el siguiente.
Por este motivo, una lista lineal se la denomina tambien Iista enlazada.
Para construir una lista lineal primero tendremos que definir la clase
de objetos que van a formar parte de la misma. De una forma generica
el tipo definido sera de la forma:
typedef struct id tipo_objeto;
struct id
/ * declaraci6n de los miembros de la estructura d
tipo_objeto ~iguiente;
];
typedef struct datos elemento;
struct datos
{
int dato;
elemento ~iguiente;
elemento *p;
p = NuevoElemento( );
p- >siguiente = NULL;
Este ejemplo declara un tipo denominado elemento. Observamos que
uno de sus miembros es un puntero a un objeto del mismo tipo. Esto per-
mitini a un elemento creado referenciar su sucesor. La senten cia elemen-
to *p declara un puntero p a un objeto de tipo elemento. La sentencia
p=NuevoElemento( ) crea (asigna memoria para) un objeto de tipo ele-
mento, genera un puntero (direcci6n de memoria) que referencia este nue-
vo objeto y asigna esta direcci6n a la variable p. La sentencia p- >siguiente
= NULL asigna al miembro siguiente del objeto apuntado por p el valor
NULL, indicando as! que despues de este elemento no hay otro.
Una declaraci6n como p = NULL indica una lista vacfa (suponiendo
que p apunta al principio de la lista).
Un objeto puede referenciarse por mas de un puntero; y tambien un
objeto de un determinado tipo puede copiarse en otro objeto del mismo tipo.
# include <stdio.h >
# include <stdlib.h>
typedef struct datos elemento; / * declaraci6n del tipo elemento */
struct datos
[
int dato;
elemento ~iguiente;
elemento *NuevoElemento( );
void error(void);
main( )
[
elemento *P, ..q, *r;
/ * Crear dos objetos de tipo elemento apuntados por p y q
* respectivamente
*/
p = NuevoElemento( );
if (!p) error( );
q = NuevoElemento( );
if (!q) error( );
p->dato = 5;
#J = *P / * copia el objeto apuntado por p en el objeto
apuntado por q */
r = q; / * r apunta al mismo objeto que q */
printj("%d  n': r- >dato * 2) / * escribe 10 */
J
elemento *NuevoElemento( )
[
return ((elemento *)malloc(sizeof (elemento)));
J
1.- Recorrido de una lista.
2.- Busqueda de un clemento en la lista.
3.- Inserci6n un elemento en la lista.
4.- Borrado de un clemento de la lista.
void error(void)
(
perror(Herror: insuficiente espacio de memoria  n ");
exit(l);
}
Las operaciones que podemos realizar con listas incluyen fundamentalmente
las siguientes:
typedef struct datos elemento; / * declaracion del tipo elemento */
struct datos
{
iot dato;
elemento ~iguiente;
};
Primero se crea un elemento y despues se reasignan los punteros, tal como
se indica a continuaci6n:
q = NuevoElemento( );
q->dato = n;
q- >siguiente = p;
p = q;
/ * asignacion de valores */
/ * reasignacion de punteros */
Este concepto nos sugiere como crear una Iista. Para ello, y partiendo
de una lista vacfa, no tenemos mas que repetir la operacion de insertar un
elemento al comienzo de una lista. Veamoslo a continuacion:
elemento *P, ~;
int n;
printf((Introducir datos. Finalizar con AZ  n  n");
p = NULL;
printf((dato: ");
while (scanf((%d': &n) /= EOF)
{
q = NuevoElemento( );
q->dato = n;
q->siguiente = p;
p = q;
printf((dato: ");
J
Notar que el orden de los elementos en la lista, es el inverso del orden
en el que han llegado.
La insercion de un elemento en la lista, a continuacion de otro elemento
apuntado por p, es de la forma siguiente:
q = NuevoElemento( );
q->dato = x; / * valor insertado */
q->siguiente = p- >siguiente;
p- >siguiente = q;
La inserci6n de un elemento en la lista antes de otro elemento apunta-
do por p, se hace insertando un nuevo elemento detnis del elemento apun-
tado por p, intercambiando previamente los valores del nuevo elemento y
del elemento apuntado por p.
q = NuevoElemento( );
*lJ = *P;
p->dato = x;
p- >siguiente = q;
Para borrar el sucesor de un elemento apuntado por p, las operaciones a
realizar son las siguientes:
q = p- >siguiente;
p- >siguiente = q- >siguiente;
jree(q);
Para borrar un elemento apuntado por p, las operaciones a realizar
son las siguientes:
q = Py siguiente;
*P = *q;
jree(q);
Como ejercicio, escribir la secuencia de operaciones que nos permitan
barrar el ultimo elemento de una lista.
Recorrido de una Iista cuyo primer elemento esta apuntado
par p
Supongamos que hay que realizar una operaci6n con todos los elementos
deuna lista, cuyo primer elemento esta apuntado por p. Por ejemplo, es-
cribir el valor de cada elemento de la lista. La secuencia de operaciones
es la siguiente:
q = p; / * salvar el puntero al comienzo de la !ista */
while (q /= NULL)
{
printj("%d ': q- >dato);
q = q- >siguiente;
}
La busqueda es secuencial y termina cuando se encuentra el elementa,0
bien, cuando se llega al final de la lista. .
~q = p;
printf(t<valor: "); scanf(t<%d': &x);
while (q /= NULL && q->dato /= x)
q = q- >siguiente;
Como ejercicio, realizamos a continuaci6n un programa que nos per-
mita crear una lista clasificada, en la cual cada elemento conste de dos cam·
pos: uno que contenga un numero entero y otro que sea un puntero a un
elemento del mismo tipo.
1. Afiadir un elemento. Esta funci6n comprendeni dos casos: inser·
tar un elemento al principio de la lista 0 insertar un elemento des·
pues de otro.
2. Borrar un elemento de la lista. Esta funci6n buscani el elemento
a borrar y despues 10 borrani. Hay que distinguir si se trata de
la cabecera 0 de un elemento cualquiera.
# include <stdio.h>
# include <std!ib.h>
# include <conio.h >
# define ListaVacia (cabecera
/ * Lista simplemente enlazada.
* Cada elemento contiene un n° entero.
*/
typedef struct datos elemento;
struct datos
/ * tipo elemento */
/ * elemento de una !ista de enteros */
[
fit dato;
elemento ~iguiente;
void error(void)
[
perror(Herror: insuficiente espacio de memoria ");
exit(l);
l
elemento *NuevoElemento( )
[
elemento *lJ = (elemento *)malloc(sizeof(elemento));
if (!q) error( );
return(q);
l
/ * Funciones prototipo */
void menu(void);
void anadir(elemento **, int);
void borrar(elemento **, int);
elemento *buscar(elemento *, int);
void visua!izar(elemento *);
main( )
{
elemento ..cabecera = NULL;
elemento .,q;
iot opcion, dato, k=10;
while (1)
{
do
(
system(Hcls");
menu( );
opcion = getche( );
~
while (opcion < '1' II opcion > '5');
system(' 'cis");
switch (opcion)
(
case '1':
printj(Hanadir dato: "); scanj(H%d': &dato);
anadir(&cabecer~ datok
break;
case '2':
printj(Hborrar dato: "); scanj(H%d': &dato);
borrar(&cabecera, dato);
break;
case '3':
printj(Hbuscar dato: "); scanj(H%d': &dato);
q = buscar(cabecera, dato);
if (q)
q- >dato + = k;
else
printj(HLista vacfa  n");
break;
case '4': visualizar(cabecera); break;
case '5': exit(O);
}
printj(H  nPulse una tecla para continuar"); getch( );
}
}
void menu( )
(
printj("  n  t1.
printjt'  n  t2.
printj("  n  t3.
printj("  n  t4.
printj("  n  t5.
printf("  n  t
)
Afiadir un elemento  n");
Borrar un elemento  n ");
Buscar un elemento  n");
Visualizar la lista  n");
Salir  n");
Elija la opcion deseada ");
/ * Introducir un elemento ordenadamente en la lista */
void anadir(elemento *~ab, int dato)
(
elemento *cabecera = *cab;
elemento ~ctual = cabecera, ~nterior = cabecera, *q;
if (Lista Vacia)
(
cabecera = NuevoElemento( );
cabecera->dato = dato;
cabecera->siguiente = NULL;
*cab = cabecera;
return;
)
/ * Entrar en la lista y encontrar el punta de insercion */
while (actual /= NULL && dato > actual->dato)
(
anterior = actual,'
actual = actual- >siguiente;
)
/ * Dos casos:
* 1) Insertar al principio de la lista
* 2) Insertar despues de anterior (incluye insertar al final)
*/
q = NuevoElemento( );
if (anterior = = actual)
(
q->dato = dato;
q->siguien te = cabecera;
cabecera = q;
/ * se genera un nuevo elemento */
/ * insertar al principio */
else
(
q'- >dato = dato;
q- >siguiente = actual,'
anterior- >siguiente = q;
1
..cab = cabecera;
/ * Encontrar un dato y borrarlo */
void borrar(elemento **cab, int dato)
{
elemento *cabecera = *cab;
elemento *Uctual= cabecera, *Unterior=cabecera;
if (Lista Vacia)
(
printf(HLista vacfa  n ");
return;
while (actual != NULL && dato != actual->dato)
{
anterior = actual;
actual = actual- >siguiente;
if (anterior = = actual) / * borrar el elemento de cabecera */
cabecera = cabecera- >siguiente;
else / * borrar un elemento no cabecera */
anterior- > siguiente = actual- > siguiente;
freeractual);
*cab = ocabecera;
/ * Buscar un elemento determinado en la lista */
elemento *buscar(elemento ~abecera, int dato)
[
elemento *actual = cabecera;
while (actual != NULL && dato != actual->dato)
actual = actual- >siguiente;
return (actual);
J
/ * Visualizar la lista */
void visualizar(elemento *cabecera)
[
elemento *actual = cabecera;
if (ListaVaria)
printf("Lista vacfa  n");
else
[
while (actual != NULL)
[
printf("%d ': actual- >dato);
actual = actual- >siguiente;
J
printf("  ri");
Una pila es una lista lineal en la que todas las inserciones y supresiones
(y normalmente todos los accesos), se hacen en un extremo de la lista. Un
ejemplo de esta estructura es una pila de platos. En ella, el aiiadir 0 quitar
platos se hace siempre por la parte superior de la pila. Este tipo de listas
inserciones
y
supresiones
reciben tambien el nombre de listas LIFO (last in first out - ultimo en en-
trar, primero en salir).
Las operaciones de insertar y suprimir en una pila, son conocidas en
los lenguajes ensambladores como push y pop respectivamente.
La operaci6n de recuperaci6n de un elemento de la pila, 16 elimina
de la misma.
Como ejemplo de utilizaci6n de una pila, vamos a simular una calcu-
ladora capaz de realizar las operaciones de +, -, * y /. La mayoria de las
calculadoras aceptan la notaci6n infija y unas pocas la notaci6n postfija.
En estas ultimas, para sumar 10y 20 introduciriamos primero 10, despues
20 y por ultimo el +. Cuando se introducen los operandos, se colocan en
una pila y cuando se introduce e1operador, se sacan dos operandos dela
pila. La ventaja de la notaci6n postfija es que expresiones complejas pue-
den evaluarse facilmente sin mucho c6digo.
La calculadora de nuestro ejemplo utiliza la notaci6n postfija, par 10
que hemos desarrollado dos funciones: push y pop. La funci6n push intro-
duce un valor en la pila y la funci6n pop saca un valor de la pila.
2. Analiza op; si se trata de un operando 10 mete en la pila utilizan-
do la funci6n push( ); y si se trata de un operador saca, utilizando
la funci6n pop( ), los dos ultimos operandos de la pila, realizala
operaci6n indicada por dicho operador y mete el resultado enla
pila para encadenarlo con otra posible operaci6n.
# include <stdio.h >
# include <stdlib.h>
# define PitaVacia (cima = = NULL)
typedef struct datos elemento; / * tipo elemento */
struct datos / * estructura de un elemento de la pita */
!
float dato;
elemento *Siguiente;
void error(void)
!
perror("error: insuficiente espacio de memoria ");
exit(l);
}
elemento *NuevoElemento( )
!
elemento *q = (elemento *)malloc(sizeof(elemento));
if (!q) error( );
return (q);
}
void push(elemento **P, float x);
float pop(elemento **p);
/ * afiadir un dato a la pita */
/ * sacar un dato de la pita */
main( ) / *funci6n principal */
!
elemento *cima = NULL;
float a, b;
char op[81];
system("cls ");
printf("Calculadora con las operaciones: + - * /  n");
printj("Los datos serdn introducidos de la forma:  n");
printf(H>operando 1 n");
printftc>operando 2  n");
printf(H>operador  n  n");
printf(HPara salir pulse q  n  n");
do
[
printf(H> ");
gets(op);
switch (*op)
[
b = pop(&cima); a = pop(&cima);
printf(H%g  n': a + b);
push( &cima, a+b );
break;
case c_':
b = pop(&cima); a = pop(&cima);
printf(H%g  n': a - b);
push( &cima, a-b );
break;
case C *':
b = pop(&cima); a = pop(&cima);
printf(H%g  n': a * b);
push( &cima, a*b );
break;
case 'i':
b = pop(&cima); a = pop(&cima);
if (b = = 0)
(
printf(H  nDivisi6n por cero  n ");
break;
J
printf(H%g  n': a / b);
push (&cima, a/b);
break;
default :
push(&cima, atof(op));
J
J
while (*op != cq');
J
/ *Afiadir un dato a fa pila */
void push(efemento **P, float x)
I
efemento *Q, ..cima;
q = NuevoEfemento( );
q->dato = x;
q- >siguiente = cima;
cima = q;
/ * Recuperar ef dato de fa cima de fa pi/a */
float pop(efemento **p)
I
efemento ..cima;
float x;
if (Pila Vacia)
I
printft'  nerror: pop de una pila vacfa  n ");
return 0;
I
else
x = cima- >dato;
*p = cima- >siguiente;
jree(cima);
return (x);
J
J
Calculadora con las operaciones: + - * /
Los datos senin introducidos de la forma:
>operando 1
>operando 2
>operador
> 4.2
> 5
> *
21
>10
> -
11
>q
Una cola es una lista lineal en la que todas las inserciones se hacen por
un extrema; todas las supresiones (y normalmente todos los accesos) se hacen
por el otro extremo de la lista. Por ejemplo, una fila en un banco. Este
tipo de listas reciben tambien el nombre de listas FIFO (first in first out
- primero en entrar, primero en salir). Este orden, es la unica forma de in-
sertar y recuperar un elemento de la cola.
( -1_H_H_H_I-
Tener en cuenta que la operaci6n de recuperaci6n de un e1emento de
la cola, 10 elimina de la misma.
Considerese el problema de almacenar las citas diarias, con el fin de
ejecutarlas en el dia y hora sefialados. Cuando una de las citas se efectua,
se quita de la lista. El program a que se expone a continuaci6n recoge este
tipo de sucesos u otros. En el, empleamos dos funciones: introducir() y
realizar( ). La funci6n introducir( ) nos permite afiadir nuevos sucesos a
la cola y la funci6n realizar( ) saca un suceso de la cola.
1. Presenta un menu con las opciones de: Introducir un suceso, Rea-
lizar un suceso y Salir. A continuaci6n nos solicita que elijamos
una opci6n, 10 que da lugar a que se Harne a la funci6n corres-
pondiente.
2. La funci6n introducir( ) comprendeni dos casos: afiadir un ele-
mento a una cola vacia 0 afiadir un elemento al final de la cola.
3. La funci6n realizar( ) devuelve como resultado el suceso recupe-
rado del principio de la cola. Si la cola estuviese vacia, se indicani
con un mensaje.
# include <stdio.h>
# include <stdlib.h >
# include <string.h >
# include <conio.h >
typedef struct datos elemento; / * tipo elemento */
struct datos / * estructura de un elemento de la cola */
[
char suceso[81];
elemento *Siguiente;
void error(void)
[
perror("error: insuficiente espacio de memoria");
exit(l);
J
elemento *NuevoElemento( )
{
elemento *q = (elemento *)malloc(sizeof(elemento));
if (!q) error( );
return (q);
J
void menu(void);
void introducir(elemento **, elemento **, char [ J);
char *realizar(elemento **, elemento **);
main( ) / *funci6n principal */
{
elemento *principio, 4inal;
char opcion, suceso[81];
principio = final = NULL;
while (1)
do
(
system ("cis ");
menu( );
opcion = toupper(getche( ));
J
while (opcion!= T && opcion!= 'R' && opcion != 'S');
system ("cis ");
switch (opcion)
(
case '1':
printj("  nlntroduzca suceso: ' ),
gets(suceso);
introducir( &principio, &final, suceso);
break;
case 'R':
strcpy(suceso, realizar(&principio, &final));
if (*Suceso)
printj("  nRealizando el suceso %s  n'~ suceso);
print/(U  nPulse una tec!a para continuar  n "); getch( );
break;
case '5':
exit(O);
)
)
)
void menu{ ) / * menu de opciones */
(
printj(CC n  t Introducir suceso  n ");
printj(CC n  t Realizar suceso  n");
printj(CC n  t 5alir  n");
printf(cc  n  t Elija la opcion deseada ( 1,R, 5): ");
)
/ * Afiadir un dato a la cola */
void introducir(elemento **P, elemento **f, char suceso[ })
(
elemento *pc, 4c, *q;
pc = *P;
fe = *f,"
/ *principio de la cola */
/ *final de la cola */
q = NuevoElemento( );
strcpy(q- >suceso, suceso);
q->siguiente = NULL;
if (fc = = NULL)
pc = fc = q;
else
fc = fc- >siguiente q;
/ * Recuperar un dato de la cola */
char *realizar(elemento **P, elemento **f)
{
elemento *pc, 4c, *q;
char ~uceso;
pc = *p; / *principio de la cola */
fc = 4; / *final de la cola */
if ( pc != NULL)
{
q = pc;
suceso = (char *)malloc(strlen( q- >suceso) +1);
strcpy(suceso, q- >suceso);
pc = pc- >siguiente;
if (pc = = NULL)
fc = NULL; / * habra un solo suceso */
free(q);
*p = pc;
4 = fc;
return (suceso);
}
printf(CC  nNo hay sucesos.  n ");
return 0;
}
Una lista circular es una lista lineal, en la que el ultimo elemento enlaza
con el primero. Entonces es posible acceder a cualquier elemento de la lis-
ta desde cualquier punta dado. Las operaciones sobre una lista circular re-
sultan mas sencillas, ya que se evitan casos especiales.
Cuando recorremos una lista circular, diremos que hemos llegadoal
final de la misma, cuando nos encontremos de nuevo en el punta de parti-
da; suponiendo, desde luego, que el punta de partida se guarda de alguna
manera en la.lista, por ejemplo con un puntero fijo al mismo.
Otra posible soluci6n al problema anterior seria poner en cada !isla
circular, un elemento especial identificable como lugar de parada. Esteele-
mento especial recibe el nombre de elemento de cabecera de la Usta. Esto
presenta la ventaja de que la lista circular no estara nunca vacia.
Una lista circular con un puntero al ultimo elemento, es equivalente
a una lista lineal recta con dos punteros, uno al principio y otro al final.
CABECERA
~
Como ejemplo de utilizaci6n de listas circulares, realizaremos la suma
de ecuaciones algebraicas 0 polin6micas de las variables x, y, z. Por ejemplo:
Cada polinomio sera representado como una lista en la que cad a ele-
mento representa un termino no nulo, como se indica a continuaci6n:
Aqui COEFICIENTE es el coeficiente del termino xA yB zC. Suponemos
que los coeficientes y exponentes est an dentro de los rangos permitidos.
La notaci6n ABC se utilizara para representar eI campo ±ABC de cada
elemento tratado como un numero entero. El signo de ABC sera siempre
positivo, excepto para el elemento de cabecera. Para este elemento, ABC=-l
y COEFICIENTE = O. Los elementos de la lista apareceran sobre la mis-
ma en orden decreciente del campo ABC, siguiendo la direcci6n de los en-
laces. Por ejemplo, el polinomio 2x3y + 4xy3 - y4 se representaria:
CABECERA
~
A continuaci6n se muestra el programa correspondiente para sumar
dos polino.mios, almacenados en dos listas circulares, denominadas par polP
y polQ respectivamente. El campo ABC se corresponde con un entero igual
A* 100 + B* 10 + C. Esto limita los exponentes a un digito. Si deseamos
utilizar dos digitos, necesitariamos un entero de 6 digitos. Farmando de
esta manera el campo ABC, es muy sencillo para aplicaciones posteriores,
descomponerlo en Ios exponentes individuales.
1. Leer polinomio. Esta funci6n lee los terminos correspondientes a
un polinomio determinado. La lectura se hace en orden creciente
de Ivs exponentes ABC. Como consecuencia se crea una Iista cir-
cular, que inicialmente constaba solamente del elemento cabecera.
2. Inicializar. Esta funci6n situa un puntero (actual) sobre el primer
termino de un polinomio.
3. Comparar. Esta funci6n compara cada termino del polinomio P
con los terminos del polinomio Q con el fin de sumar sobre Q los
terminos de igual exponente, y afiadir a Q en orden decreciente
de ABC, los terminos de P que no esten en Q. Para estas opera-
ciones se utilizan las funciones "sumar coeficientes" e "insertar
nuevo termino" respectivamente.
4. Eliminar termino. Esta funci6n elimina un termino nulo del poli-
nomio Q, resultado de sumar un termino de P con el correspon-
diente termino de Q.
La terminologia empleada en el programa se interpreta de la forma
siguiente:
polP: identifica al polinomio P. Es una estructura que contiene tres punte-
ros: cabecera que apunta el elemento cabecera de la lista que contiene al
polinomio P, actual que apunta el termino del polinomio sobre el que esta-
mos trabajando y anterior que apunta al termino anterior al actual.
poIP->actual->siguiente: hace referencia al campo siguiente del elemen-
to apuntado por el puntero actual del polinomio P.
/ * Listas circulares. Suma de ecuaciones algebraicas.
* Cada termino es funci6n de las variables x, y, z
* con exponentes a, b y c respectivamente, en el rango 0 a n.
*/
# include <stdio.h >
# includ.e.--<std!ib.h>
typedef struct datos elemento; / * tipo elemento */
typedef elemento * pelemento; / * tipo puntero a un elemento */
struct datos / * estructura de un elemento de la !ista */
{
float coeficiente;
int abc;
pelemento siguiente;
];
/ * coeficiente del termino en xyz */
/ * exponentes de x, y, Z d
typedef struct !ista ListCir;
struct !ista
{
pelemento cabecera;
pelemento anterior;
pelemento actual;
J;
/ * cabecera de la !ista circular */
/ * elemento anterior al actual */
/ * elemento actualmente apuntado */
void error(void)
{
perror("error: insuficiente espacio de memoria");
exi/(l);
J
pelemento NuevoElemento( )
[
pelemento q = (pelemento )malloc(sizeof(elemento));
if (!q) error( );
return (q);
l
void leer---po!inomio(ListCir *);
void inicializar(ListCir *);
void comparar(ListCir *, ListCir *);
void sumar _coeficientes(ListCir *, ListCir *);
void insertar_nuevo_termino(ListCir *, ListCir *);
void e~iminar_termino(ListCir *);
void es'cribir---po !inom io(ListCir);
main( )
{
ListCir polp, polQ;
/ * LEER POLINOMIOS polP y polQ */
leer---po!inomio(&polP);
leer---polinomio(&polQ);
/ * INICIALIZAR */
inicia!izar(&polP);
inicia!izar(&polQ);
/ * COMPARAR */
comparar(&polp, &polQ);
/* ESCRIBIR POLINOMIO RESULTANTE Q */
escribir---po!inomio(polQ);
l
void leer---polinomio(ListCir *polX)
/ * Los nodos de la !ista se colocardn en orden decreciente
* del campo abc, por 10 que hay que introducirlos en
* orden inverso, esto es en orden creciente.
*/
[
iot abc,'
float coef,'
pelemento q,'
/ * Elemento de cabecera */
polX- >cabecera = NuevoElemento( ),'
polX- >cabecera- >coejiciente = 0,'
polX- >cabecera- >abc = -001,'
polX- >cabecera- >siguiente = polX- >cabecera,'
polX- > anterior = polX- > actual = NULL,'
~
/ * Elementos restantes */
printj(t<Introducir los term inos del polinomio en 
orden creciente de abc (xAa . yAb . ZAC) n  n n),'
printjt'Para jinalizar, teclear coejiciente = 0  n  nn),'
printjt 'coejiciente: "),'
scanf(t<%f': &coej),'
while (coej)
[
printf(t<exponentes abc (sin espacios).· n),'
scanf(t<%d': &abc),'
q = NuevoElemento( ),'
q- > coejiciente = coef,'
q- >abc = abc,'
q- >siguiente = polX- >cabecera- >siguiente,'
polX- >cabecera- >siguiente = q,'
printf(t<coejiciente: n),'
scanf(t<%f': &coej),'
J
J
/ * Inicializar proceso */
void inicializar(ListCir *poIX)
[
polX- >anterior = polX- >cabecera,'
polX- >actual = polX- >cabecera- >siguiente,'
J
/ * Comparar los terminos de los polino.mios polP y polQ */
void comparar(ListCir *polp, ListCir *poIQ)
{
while (!(poIP->actual->abc < 0))
{
while (poIP- >actual- >abc < polQ- >actual- >abc)
{
polQ- >anterior = polQ- >actual;
polQ- >actual = polQ- >actual- >siguiente;
}
if (poIP- >actual- >abc = = polQ- >actual- >abc)
sumar _coejfntes(po,P, poIQ);
else h polP- >actual- >abc > polQ- >actual- >abc */
{
insertar_nuevo_termino(polp, poIQ);
polP- >actual = polP- >actual- >siguiente;
}
}
}
/ * Sumar terminos con exponentes iguales; uno de P y otro de Q */
void sumar _coejicientes(ListCir *polP, ListCir *poIQ)
{
if ( polP- >actual- >abc < 0)
return;
else
{
polQ- >actual- >coejiciente + = polP- >actual- >coejiciente;
if ( polQ- >actual- >coejiciente = = 0)
{
eliminar_termino(poIQ);
polP- >actual = polP- >actual- >siguiente;
}
else
{
polP- >actual = polP- >actual- >siguiente;
polQ- >anterior = polQ- >actual,'
polQ- >actual = polQ- >actual- >siguiente;
}
}
}
/ * El polinomio P contiene un terminG que no existe en Q d
void insBrtar_nuevo_termino(ListCir *polp, ListCir *poIQ)
{
/ * Se inserta antes del actual */
pelemento q;
q = NuevoElemento( );
q- >coejiciente = polP- >actual- >coejiciente;
q- >abc = polP- >actual- >abc;
q- > siguiente = polQ- > actual;
polQ- >anterior = polQ- >anterior- >siguiente = q;
return; ) / * retornar a comparar */
/ * Eliminar el terminG de coejiciente nulo */
void eliminar_termino(ListCir *poIQ)
{
pelemento q;
q = polQ- >actual;
polQ- >actual = polQ- >actual- >siguiente;
polQ- >anterior- >siguiente = polQ- >actual;
jree(q); / * liberar la memoria ocupada por el terminG nulo */
return; / * retornar a sumar _coejicientes */
void escribir-polinomio(ListCir polQ)
{
printjt'  n  nSuma de los polinomios:  n  n");
polQ.cabecera = poIQ.cabecera- >siguiente;
while (polQ.cabecera- >abc != -1)
{
printf("coej" % +g exps. de x y z 0/003d n':
poIQ.cabecera- >coejiciente, poIQ.cabecera- >abc);
polQ.cabecera = polQ.cabecera- >siguiente;
J
J
Una lista doblemente enlazada, es una lista lineal en la que cada elemento
tiene dos enlaces, uno al elemento siguiente y otro al elemento anterior.
Esto permite leer la lista en cualquier direcci6n.
Las operaciones sobre una lista doblemente enlazada, normalmente
se realizan sin ninguna dificultad. Sin embargo, casi siempre es mucho mas
facilla manipulaci6n de las mismas, cuando se afiade un elemento de ca·
becera y existe un doble enlace entre el ultimo elemento y el primero. Esta
estructura recibe el nombre de Iista circular doblemente enlazada.
Como ejemplo, vamos a construir una lista doblemente enlazada or-
denada ascendentemente; cada elemento introducido se colocara automa-
ticamente en el lugar que Ie corresponde. El programa consta fundamen-
talmente de tres funciones: insertar( ), borrar( ) y visualizar( ).
La funci6n insertar( ) comprende los casos: insertar un elemento al
principio, insertar un elemento entre otros dos e insertar un elemento al
final. La funci6n borrar( ) comprende: borrar el primer elemento y borrar
un elemento cualquiera que no sea el primero.
# include <stdio.h>
# include <stdlib.h >
# include < string.h >
# include <eonio.h >
# define Lista Vacia (listaD- >prine
typedef struct datos elemento; / * tipo elemento */
typedef elemento * pelemento; / * tipo puntero a un elemento */
struct datos / * estruetura de un elemento de la lista */
(
pelemento siguiente;
char clave[12];
pelemento anterior;
J;
typedef struct !ista ListDob;
struct !ista
(
pelemento prine; )
pelemento finat:
J;
/ * prineipio de la !ista d
/ *final de la !ista */
void error(void)
(
error("error: insuficiente espacio de memoria ");
exit(l);
J
pelemento NuevoElemento( )
(
pelemento q = (pelemento )malloe(sizeof(elemento));
if (!q) error( );
return (q);
J
void insertar(ListDob *, char [ J);
void borrar(ListDob *, char [ J);
void visua!izar_!ista(ListDob);
void menu(void);
main( )
(
ListDob !istaD;
char opcion, clave[12];
listaD.princ
while (1)
{
do
{
system ( 'cls' ');
menu( );
opcion = toupper(getche( ));
]
while (opcion!= '1' && opcion !=
opcion!= 'V' && opcion !=
system("cls");
switch (opcion)
{
case '1': ~
printf("  nlntroduzca fa clave a aiiadir: ");
gets(clave);
insertar(&listaD, clave);
break;
case 'B':
printf("  nlntroduzca fa clave a borrar: ");
gets(clave);
borrar(&listaD, clave);
break;
case 'V':
visualizar_lista(listaD);
printf("  nPufse una tecla para continuar "); getch( );
break;
case '5':
'B' &&
'5');
]
]
]
/ * Aiiadir un dato a fa lista */
void insertar(ListDob *fistaD, char clave! })
{
pefemento q, pactuaf, panterior;
/ * Generar un efemento */
q = NuevoEfemento( );
strepy(q- >clave, clave);
q- >anterior = q- >siguiente
if (Lista Vacia)
(
listaD- >prine
return;
J
/ * busear la posicion donde hay que insertar el pelemento */
paetual = panterior = listaD- >prine;
while (paetual != NULL && stremp( clave, paetual- >clave) > 0)
(
panterior = paetual;
paetual = paetual- >siguiente;
J ~
if (panterior = = paetual)
(
q->siguiente = listaD- >prine;
listaD->prine = paetual- > anterior = q;
J
else
(
q->anterior = panterior;
q->siguiente = paetual,'
panterior- >siguiente = q;
if (paetual) paetual- > anterior = q;
/ * paetual sera NULL cuando se inserta al final */
/ * insertar despues de panterior */
/ * incluye insertar al final */
/ * Eneontrar una determinada clave y borrar el elemento */
void borrar(ListDob *listal), char clave! J)
[
pelemento panterior, paetual;
if (ListaVacia)
return;
/ * Entrar en la lista y eneontrar el elemento a borrar */
panterior = paetual = listaD- >prine;
while (pactual != NULL && strcmp( clave, pactual- >clave) != 0)
{
panterior = pactual;
pactual = pactual- >siguiente;
J
/ * Si el dato no se encuentra retornar */
if (pactual = = NULL)
{
printjt'%s no estd en la !ista  n': clave);
printj("  nPulse una tecla para continuar H); getch( );
return;
J
/ * Si el dato se encuentra, borrar el elemento */
if (panterior = = pactual) / * el elemento estd al principia */
{ -------
!istaD- >prine = !istaD- >princ- > siguiente;
if (!istaD- >prine) !istaD- >princ- > anterior = NULL;
/ * Si principio es igual a NULL habfa un solo elemento */
J
else / * borrar un elemento que no estd al principia */
{
/ * Modijicar el enlace siguiente */
panterior- >siguiente = pactual- >siguiente;
/ * Modijicar el enlace anterior excepto para el ultimo */
if (pactual- >siguiente)
panterior- > siguiente- > anterior = pactual- > anterior;
J
jree(pactual);
J
/ * Visua!izar el contenido de la !ista */
void visua!izar_!ista(ListDob !istaD)
{
pelemento pactual = !istaD.princ;
while (pactual != NULL)
{
printf("%s  n': pactual- >clave);
pactual = pactual- >siguiente;
J
J
void menu( )
I
printj("  n  t
printj("  n  t
printf("  n  t
printj("  n  t
printj("  n  t
J
Introducir un nuevo elemento  n");
Borrar un elemento  n ");
VisuaUzar la Usta n ");
SaUr n ");
EUja la opcion deseada ( I, B, v, S): ");
ListDob es una estructura que identifica la lista doblemente enlazada
que estamos creando. Contiene dos punteros que definen perfectamente
la lista:prine que apunta al primer elemento, y final que apunta al ultimo
elemento. Para realizar las operaciones de inserci6n y borrado utilizamos
dospunteros auxiliares: paetual que apunta al elemento identificado, y pan-
teriar que apunta ~lemento anterior al identificado.
Un arbol es una estructura no lineal formada por un conjunto de nodos
y un conjunto de raffias. En un arbol existe un nodo especial denominado
raiz.Un nodo del' que sale alguna rama, recibe el nombre de nodo de bi-
furcacion 0 nodo rama y un nodo que no tiene ramas recibe el nombre de
Dodo terminal 0 nodo hoja.
nivel 0 ralz
nivel 1 nodo de
bifurcacidn
nivel 2 nodo terminal
Arbol
De un modo mas formal, diremos que un arbol es un conjunto finito
de uno 0 mas nodos tales que:
b) Los nodos restantes estan agrupados en D > 0 conjuntos disjuntos
AI' ... ,An' cada uno de los cuales es a su vez un arbol, que recibe
el nombre de subarbol de la raiz.
La definicion dada es recursiva, es decir, hemos definido un arbol como
un conjunto de arboles. Esta es la forma mas apropiada de definir un arbol.
De la definicion se desprende, que cada node de un arbol es la raiz
de algun subarbol contenido en la totalidad del mismo. El numero de ra-
mas de un node recibe e1 nombre de grado del nodo. El DiveI de un nodo
respecto al node raiz se define diciendo que la raiz tiene nivel 0 y cualquier
otro node tiene un nivel igual a la distancia de ese node al node raiz. EI
maximo de los niveles se denomina profuDdidad 0 altura del arbol.
Es utillimitar los arboles en el sentido de que cad a node sea a 10 sumo
de grade 2. De esta forma cabe distinguir entre subarbol izquierdo y su-
barbol derecho de un nodo. Los arboles asi formados, se denominan arbo-
les binarios. --.-/
Un arbol binario es un conjunto finito de nodos que consta de un Dodo
raiz que tiene dos sub arboles binarios denominados subarbol izquierdo y
subarbol derecho. Evidentemente, la definicion dada es una definicion re-
cursiva, es decir, cada subarbol es un arbol binario.
Las formulas algebraicas, debido a que los operadores que intervie-
nen son operadores binarios, nos dan un ejemplo de estructura en arbol
binario. La figura siguiente nos muestra un arbol que corresponde a la ex-
presion aritmetica:
Esta definicion de arbol binario, sugiere una forma natural de representar ar-
boles binarios en un ordenador: debemos tener dos enlaces (izdo y dcho) en
cada nodo, y una variable de enlace raiz que nos direcciona el arbol. Esto es:
typedef struct d~ nodo;
struct datos / * estructura de un nodo del arbol */
[
/ * declaraci6n de miembros */
nodo *izdo;
nodo *dcho;
];
Si el arbol esta vado, raiz es igual a NULL; en caso contrario, raIl
es un puntero que direcciona la raiz del arbol, e izdo y dcho son punteros
que direccionan los subarboles izquierdo y derecho de la raiz, respecti·
vamente.
Hay varios algoritmos para el manejo de estructuras en arbol. Una
idea que aparece repetidamente en estos algoritmos es la noci6n de reco·
rrido de un arboI. Este es un metoda para examinar sistematicamente los
nodos de un arbol, de forma que cada node sea visitado solamente una vez.
Pueden utilizarse tres formas principales para recorrer un arbol bina·
rio: preorden, inorden y postorden. Cuando se visitan los nodos en preor·
den, primero se visita la raiz, despues el subarbol izquierdo y por ultimo
el subarbol derecho. Cuando se visitan los nodos en iilorden, primero se
visita el subarbol izquierdo, despues la raiz y por ultimo el subarbol dere·
cho. Cuando se visitan los nodos en postorden, primero se visita el subar-
bol izquierdo, despues el subarbol derecho y por ultimo la raiz.
R
,.:..
/  preorden: R, I, D
....,.
inorden:

I, R, D
postorden: I, D, R
I D
Evidentemente, las definiciones dadas son definiciones recursivas, ya
que, recorrer un arbol utilizando cualquiera de ellas, implica recorrer sus
subarboles empleando la misma definici6n.
Si se aplican estas definiciones al arbol binario de la figura "f6rmulas
algebraicas" anterior, se obtiene la siguiente soluci6n:
Preorden:
[norden:
Postorden:
* + albc-d*e!
a+blc*d-e*!
abcl+de!*-*
El recorrido en preorden produce la notaci6n prefija; el recorrido en
inorden produce la notaci6n convencional; y el recorrido en postorden pro·
duce la notaci6n postfija 0 inversa.
Los nombres de preorden, in orden y postorden derivan del lugar en
el que se visita la raiz con respecto a sus subarboles. Estas tres formas, se
exponen a continuaci6n como tres funciones recursivas. En ellas se utiliza
la variable a significando la direcci6n de la raiz del arbol con el cual se opera.
void preorden(nodo *a)
{
if (a /= NULL)
(
/ * operaciones con el nodo a */
preorden(a- >izdo);
preorden(a- >dcho);
)
)
void inorden(nodo ~)
{
if (a /= NULL)
(
inorden(a- >izdo);
/ * operaciones con el nodo a */
inorden(a- >dcho);
)
I
void postorden(nodo ~)
{
if (a /= NULL)
(
postorden( a- >izdo );
postorden( a- >dcho );
/ * operaciones con el nodo a */
)
I
Un arbol binario de busqueda es un arbol ordenado. Las ramas de cada
nodo estan ordenadas de acuerdo con las siguientes reglas: para todo nodo
aj' todas las claves del subarbol izquierdo de aj son menores que la clave
de ai' y todas las daves del subarbol derecho de aj son mayores que la cia·
ve de aj'
Con un arbol de estas caracteristicas encontrar si un nodo de una cia·
ve determinada existe 0 no, es una operaci6n muy sencilla. Por ejemplo,
observando la figura siguiente, localizar la clave 12 es aplicar la definicion
de arbol de busqueda, esto es, si la clave buscada es menor que la clave
del nodo en el que estamos, pasamos al subarbol izquierdo de este nodo,
para continuar la busqueda y si es mayor, pasamos al subarbol derecho.
Este proceso continua hasta encontrar la clave 0 hasta llegar a un subarbol
vacfo cuya raiz tiene un valor NULL.
Como ejemplo, consideremos una secuencia de claves con el fin dede·
terminar el numero de veces que aparece cada una de ellas. Esto significa
que, empezando con un arbol vacfo, se busca cada clave en el arbol. Sise
encuentra, se incrementa su contador y si no se encuentra, se inserta en
el arbol como una nueva clave, con el contador correspondiente inicializa·
do a 1.
EI desarrollo completo se muestra a continuaci6n. EI proceso de bus-
queda, funci6n buscar( ), se formula como una funci6n recursiva. Obser·
var que al parametro formal raiz de la misma, se Ie pasa su correspondien·
te parametro actual por referencia, con el fin de hacer posible los enlaces
entre los nodos.
Una vez construido el arbol, se utiliza la fund6n visualizar_arbol( )
para visualizar e1contenido del mismo. Los nodos se visitan en inorden
y la soluci6n se presenta en forma de arbol, de acuerdo con el siguiente
esquema:
# include <stdio.h >
# include <stdlib.h >
typedef struct datos nodo;
struct datos
(
int clave;
int contador;
nodo *izdo;
nodo ~cho;
/ * tipo nodo */
/ * estructura de un nodo del arbol */
/ *puntero a la ra{z del subarbol izquierdo */
/ *puntero a la ra{z del subarbol derecho */
void error(void)
(
perror("error: insuficiente espacio de memoria ");
exit(l);
I
nodo */VuevolVodo( )
{
nodo *q = (nodo *)malloc(sizeof(nodo));
if (!ql error( );
return (q);
I
void buscar(iot, nodo **);
void visualizar_arbol(nodo *, iot);
main ( )
[
iot k;
system(Hcls");
printj (Hlntroducir claves. Finalizar con !l.Z  n  n");
printf(Hclave: ");
while (scanf (H%d': &k) /= EOF)
{
buscar(k, &raiz); / * ra[z se pasa por referencia */
printj(Hclave: ");
J
system t<cls'');
visualizar_arb 0l(raiz, 0);
1**************************************************************
Buscar una clave en el arbol
**************************************************************/
/ * Buscar por un determinado nodo y si no esta insertarlo.
* El valor para a es pasado por referencia para hacer posibles
* los enlaces entre nodo y nodo cuando se crea uno de estos.
*/
#define ArbolVacio (a = = NULL)
void buscar(iot x, nodo **raiz)
[
a = *raiz; / * razz del arbol*/
if (ArboIVacio) / * el nodo con clave x, no esta en el arbol. */
{ 1* Insertarlo */
a = NuevoNodo( );
a- >clave = x;
a- >contador = 1;
a->izdo a- >dcho NULL;
J
else
if (x < a- >clave)
/ * el valor buscado estd a la izquierda de este nodo */
buscar(x, &a->izdo);
else
{
if (x > a- >clave)
/ * el valor buscado estd a la derecha de este nodo */
buscar(x, &a->dcho);
else / * el valor buscado existe */
a->contador+ +;
/**************************************************************
Visualizar el drbol
**************************************************************/
/ * Visualizar el drbol a con margen n.
* Se emplea la forma inorden, para recorrer el drbol.
*/
void visualizar_arbol(nodo *a, iot n)
{
iot i;
if (!ArboIVacio)
(
visualizar_arbol(a- >izdo, n +1);
for (i = 1; i < = n; i + +)
printf(( ");
printf((%d(%d)  n': a- >clave, a- >contador);
visualizar_arbol( a- >dcho, n +1);
J
J
A continuacion se estudia el problema de borrar el nodo con clave x, de
un arbol que tiene las claves ordenadas. Este proceso, es una tarea faciI
si el nodo a borrar es un nodo terminal 0 si tiene un unico descendiente.
La dificultad se presenta cuando deseamos borrar un nodo que tiene dos
descendientes, ya que con un solo puntero no puede apuntarse en dos di·
recciones. En este caso, el nodo a borrar debe ser reemplazado, bien por
el nodo mas a la derecha en el subarbol izquierdo de dicho nodo, 0 bien
por el nodo mas a la izquierda en el subarbol derecho.
El proceso detallado, se presenta a continuacion y comprende lostres
casos mencionados:
1. No hay un nodo con clave igual a x.
2. El nodo con clave x tiene un unico descendiente.
3. El nodo con clave x tiene dos descendientes.
La funcion recursiva borrar_Dodo() se ejecuta solamente en el caso
3. En este caso, se desciende a 10 largo de la rama mas a la derecha del
subarbol izquierdo del nodo apuntado por q que se va a borrar, y se reern-
plaza la informacion de interes en el nodo apuntado por q por los valores
correspondientes del nodo apuntado por d, que es el nodo mas a la dere·
cha en el subarbol izquierdo. La funcion free(q) libera la memoria, delnodo
que ya no forma parte del arbol.
Observar que los valores para los panimetros formales raiz y dr, son
pasados por referenda can el fin de realizar los enlaces necesarios. La Ha-
mada a esta funci6n sera de la forma:
typedef struct datos nodo;
borrar(x, &ra{z);
/ * tipo nodo */
/ * llamada a la /unci6n */
/ * Funci6n para borrar un nodo cualquiera del arbol */
nodo *q; / * puntero al nodo a borrar */
void borrar(int x, nodo **raiz)
[
nodo *p = *raiz;
/ * Descender por el arbol de ra{z p, para buscar el nodo
* que se desea borrar
*/
if (p = = NULL) / * ;.arbol vacfo? */
printf("Esa componente no esta en el arbol  n");
else if (x < p- >clave)
borrar(x, &p- >izdo);
else if (x > p- >clave)
borrar( x, &p- >dcho );
else
[
q = p;
if (q->dcho = = NULL)
p = q->izdo;
else if (q- >izdo = = NULL)
p = q->dcho;
else
borrar_nodo(&q- >izdo);
J
jree(q);
*raiz = p;
void borrar~odo(nodo **dr)
[
nodo 4 = 4r;
/ * nodo con dos descendientes */
/ * subarbol izquierdo */
/ * Descender al nodo mas a la derecha del subarbol d */
if (d- >dcho != NULL)
borrar_nodo( &d- >dcho);
else
[
q- >clave = d- >clave;
q- >contador = d- >contador;
q = d;
d = d->izdo;
l
~r = d;
Un arbol binario esta perfectamente equilibrado si, para todo nodo, elilt!-
mero de nodos en el subarbol izquierdo y el numero de nodos en el subar-
bol derecho, difieren como mucho en una unidad.
n=1
0
Como ejemplo, considerese el problema de construir un arbol perfee-
tamente equilibrado, siendo los valores de los nodos, n numeros que seleen
de un fichero de datos, en nuestro caso del fichero predefinido stdin (fi-
chero estandar de entrada).
Esto puede realizarse facilmente distribuyendo los nodos, segun seleen,
equitativamente a la izquierda y a la derecha de cada nodo. El proceso re-
cursivo que se indica a continuaci6n, es la mejor forma de realizar esta
distribuci6n. Para un numero dado n de nodos y siendo ni (nodos a la iz-
quierda) y nd (nodos a la derecha) dos enteros, el proceso es el siguiente:
2. Generar el subarbol izquierdo con ni
misma regIa.
3. Generar el subarbol derecho con nd = n-ni-l nodos utilizando la
misma regIa.
Cada nodo del arbol consta de los siguientes miembros: clave, punte-
ro al subarbol izquierdo y puntero al subarbol derecho.
El proceso detallado se presenta a continuaci6n y comprende una fun-
cion recursiva denominada construir_arbol(), la cual construye un arbol
de n nodos.
# include <stdio.h>
# include <stdlib.h>
typedef struct datos nodo;
struct datos
[
int clave;
nodo *izdo;
nodo ~cho;
};
/ * tipo nodo */
/ * estructura de un nodo del drbol */
/ * puntero a la ra{z del subdrbol izquierdo */
/ * puntero a la ra{z del subdrbol derecho */
void error(void)
[
perror("error: insuficiente espacio de memoria");
exit(l);
}
nodo *lVuevolVodo( )
{
nodo *q = (nodo *)malloc(sizeof(nodo}};
if (!q) error( };
return (q);
J
nodo *cohstruir_arbol(int};
void visualizar_arbol(nodo *, int};
main ( ) / * Funci6n Principal */
{
system("c!s"};
printJ ('lVlimero de nodos: "); scanj("%d': &n};
printJ ("Introducir c!aves:  n  n");
raiz = construir _arbol(n}; / * construir arbol de n nodos */
system('c!s"};
visualizar_arbol(raiz, 0);
/**************************************************************
Funci6n construir arbol
**************************************************************/
/ * Construir un arbol de n nodos perJectamente equilibrado
*/
nodo *construir_arbol(int n}
{
nodo *q;
int ni, nd;
if (n = = 0)
return (lVULL);
else
ni = n / 2;
nd = n - ni - 1;
q = lVuevolVodo( );
/ * nodos del subarbol izquierdo */
/ * nodos del subarbol derecho */
printj(t<clave: "); scanf(t<%d': &q- >clave);
q- > izdo = construir _arbol(ni);
q- > dcho = construir _arbol(nd);
return (q);
}
J
/**************************************************************
Visualizar el arbol
**************************************************************/
/ * Visualizar el arbol a con margen n.
* Se emplea la forma inorden, para recorrer el arbol.
*/
void visualizar_arbol(nodo *0, int n)
!
int i;
if (a /= NULL) 1* si el arbol no esta vacfo ... */
!
visualizar_arbol(a- >izdo, n +1);
for (i = 1; i < = n; i+ +)
printj(t< ");
printj(t<%d  n': a- >clave);
visualizar_arbol(a- >dcho, n +1);
}
}
ALGORITMOS RECURSIVOS, DE
ORDENACION Y DE BUSQUEDA
En este capitulo vamos a exponer como resolver problemas muy comunes
en la vida diaria. El primer problema que nos vamos a plantear es la recur-
sion; estos son problemas cuyo planteamiento forma parte de su solucion.
El segundo problema que vamos a abordar es la ordenacion de objetos en
general; este es un problema tan comun que no necesita explicacion. Algo
tan cotidiano como una guia telefonica, es un ejemplo de una lista clasifi-
cada. Ellocalizar un determinado telefono exige una busqueda por algun
metodo. El problema de busqueda sera el ultimo que resolveremos.
Se dice que un proceso es recursivo si forma parte de si mismo 0 sea que
se define en funcion de si mismo. La recursion aparece en la vida diaria,
en problemas matematicos, en estructuras de datos y en muchos otros pro-
blemas.
La recursion es un proceso extremadamente potente, por 10 que la ana-
lizaremos detenidamente para saber cuando y como aplicarla. Esto quiere
decir que aunque un problema par definicion sea recursivo, no siempre este
sera el metoda de solucion mas adecuado.
En las aplicaciones practicas, antes de poner en marcha un proceso
recursivo, es necesario demostrar que el nivel maximo de recursion, esto
es el numero de veces que se va a llamar a sl mismo, es no solo finito, sino
realmente pequeno. La razon es que se necesita cierta cantidad de memo-
ria para almacenar el estado del proceso cada vez que se abandona, tem-
poralmente, debido a una llamada para ejecutar un proceso que es el mis-
mo. El estado en curso del proceso de calculo hay que almacenarlo, para
recuperarlo cuando se acabe la nueva ejecucion del proceso y haya que rea-
nudar la antigua.
En terminos de lenguaje de programacion, una funcion es recursiva
cuando se llama a sl misma.
Un ejemplo es la funcion de Ackerman A, la cual esta definida para
todos los valores enteros no negativos "m" y "n" de la forma siguiente:
A(O,n) = n+l
A(m,O) = A(m-l,l)
A(m,n) = A(m-l,A(m,n-l»
(m > 0)
(m,n > 0)
El pseudocodigo que nos muestra como solucionar este problema apli-
cando la recursion, es el siguiente:
A continuacion presentamos el programa correspondiente a este
problema.
<junci6n A (m,n) >
IF (m es igual a 0) THEN
devolver como resultado n +I
ELSE IF (n es igual a 0) THEN
devolver como resultado A(m-I,I)
ELSE
devolver como resultado A(m-I,A(m,n-I))
ENDIF
END <junci6n A (m,n) >
II I I I I I I I I I I I I I I I I I I I I I I I I I I main I I I I I I I I I I I I I I I I I I I I I I I I I I I I
main( )
!
int m, n, a;
printf(C<Cdlculo de A (m,n) =A (m-1,A (m,n-1))  n  n");
printf(C<Valores de m y n : ");
scanf(C<%d %d': &m, &n);
a = Ackerman(m,n);
printf(c<nnA(%d,%d) %dn': m, n, a);
]
IIIIIIIIIIIIIIIIIIIIIIIIII Ackerman IIIIIIIIIIIIIIIIIIIIIIIIII
int Ackerman(int m, int n)
!
if (m = = 0)
return n+1;
else if (n = = 0)
return Ackerman(m-1, 1);
else
return Ackerman(m-1, Ackerman(m,n-1));
Supongamos ahora que nos planteamos el problema de resolver la fun-
cion de Ackerman, pero sin aplicar la recursi6n. Esto nos exigini salvar las
variables necesarias del proceso en curso, cad a vez que la funci6n se Harne
a sfmisma, con el fin de poder reanudarlo cuando finalice el nuevo proce-
so invocado.
La mejor forma de hacer esto es utilizar una pila, con el fin de alma-
cenar los valores "m" y "n" cada vez que se invoque la funci6n para una
nueva ejecuci6n y tomar estos valores de la cima de la pila, cuando esta
nueva ejecuci6n finalice, con el fin de reanudar la antigua.
<funcion A (m,n) >
Declarar un array para utilizarlo como una pita, con el fin de
almacenar los valores de: m,n
Inicializar la pita con los valores m,n
DO
Tomar los datos de la parte superior de la pita
IF (m es igual a 0) THEN
meter en la pila el valor: n
ELSE IF (n es igual a 0) THEN
meter en la pita los valores: m-l,l
ELSE
meter en la pita el valor: m-l
meter en la pita los valores: m,n-l
ENDIF
WHILE (p sea distinto de 0)
devolver como resultado el valor n +1
END <fun cion A (m,n) >
A continuaci6n presentamos el programa correspondiente a este
problema.
# include <stdio.h>
# include <stdlib.h>
typedef struct datos elemento;
typedef elemento *pelemento;
struct datos
int m,n;
pelemento siguiente;
};
void error(void)
{
perror("error: no hay suficiente espacio en la pita  n  n ");
exit(l);
I
pelemento NuevoElemento( )
[
pelemento q = (pelemento)malloc(sizeof(elemento));
if (!q) error( );
return (q);
J
int Ackerman(int, int);
void mete-pi/a(pelemento *, int, int);
void saca-pi/a(pelemento *, int *, int *);
//////////////////////////// main ////////////////////////////
main( )
[
int m, n, a;
printjt'Cdlculo de A(m,n)=A(m-1,A(m,n-1))  n  n");
printf("Valores de m y n : ");
scanf("%d O/Od':&m, &n);
a = Ackerman(m,n);
printj(" n  nA(%d,%d) %d n': m, n, a);
J
////////////////////////// Ackerman //////////////////////////
int Ackerman(int m, int n)
[
pelemento pi/a = NULL; / * pi/a de elementos (m,n) */
int Ackerman_m_n;
mete-pila(&pi/a, m, n);
while (1)
[
/ * tomar los datos de la cima de la pi/a */
saca-pi/a(&pi/a, &m, &n);
if (m = = 0) h resultado para un elemento A(m,n) calculado d
[
Ackerman_m_n = n +1;
if (pi/a)
[
saca-pila(&pi/a, &m, &n);
mete-pi/a(&pi/a, m, Ackerman_m_n);
J
else
return (Ackerman_m_n);
l
else if (n = = 0)
mete-pi/a(&pi/a, m-1, 1);
else
[
mete-pi/a(&pi/a,m-1,Ackerman_m_n); / * n =Ackerman(m,n-l) */
mete-pi/a(&pi/a, m, n-1);
l
l
l
/////////////////////// meter en fa pi/a ///////////////////////
void mete-pi/a(pefemento *P, int m, int n)
[
pefemento q;
q = NuevoEfemento( );
q->m = m, q->n = n;
q- >siguiente = *P;
*p=q;
//////////////////////// sacar de fa pi/a //////1/////////////////
void saca-pi/a(pefemento *P, int *pm, int *pn)
[
pefemento q = *P; / * cabecera de fa pi/a ./
if (q = = NULL)
[
printj("  nLista vacfa  n ");
exit(2);
l
else
[
*pm = q->m, *pn = q->n;
*P = q- >siguiente;
jree(q);
l
l
Un proceso en el cual es realmente eficaz aplicar la recursi6n es el pro-
blema de las Torres de Hanoi. Este problema consiste en tres barras verti-
cales A, Bye y "n" discos, de diferentes tamafios, apilados inicialmente
sabre la barra A, en orden de tamafio decreciente. El abjetivo es mover los
discas desde la barra A a la barra C, bajo las siguientes reglas:
Una posible soluci6n, es el algoritmo recursivo que se muestra a con-
tinuaci6n:
nO discos origen centro destino
inicialmente n A B C
punta 1 n-l A C B
punta 2 1 A B C
punta 3 n-l B A C
<funcion mover(n_discos, A, B, C) >
IF (n_discos es mayor que 0) THEN
mover(n_discos-l, A, C, B)
mover disco de A a C
mover(n_discos-l, B, A, C)
ENDIF
END <funcion A (m,n)>
A continuaci6n presentamos el program a correspondiente a este
problema.
I I I I I I I I I I I II I I I I I I I I I I I I I I I main I I I I I / I I I I I I II I I II I I I IIIIIII
main( )
{
printj(HN° de discos: ");
scanf(H%d': &n_discos);
movimientos = mover(n_discos, ~: 'B: 'C');
printf(H  nmovimientos efectuados: %d  n': movimientos);
I I I I I I I I I I I I I I I I I I I I I I I I I I I I mover I I I I I I I I I I I I I I I I I I I I I I I I I 11/
iot mover(iot n_discos, char a, char b, char c)
{
if (n_discos > 0)
{
mover(n_discos-l, a, c, b);
printj(Hmover disco·de %c a %c  n': a, c);
movimientos+ +;
mover(n_discos-l, b, a, c);
J
return movimientos;
Como ejercicio se propone realizar este mi~mo problema, pero sin uti-
lizar recursi6n.
Uno de los procedimientos mas comunes y titiles en el procesamiento de
datos, es la clasificaci6n u ordenaci6n de los mismos. Se considera orde-
nar al proceso de reorganizar un conjunto dado de objetos en una secuen-
cia determinada. El objetivo de este proceso general mente es facilitar la
busqueda de uno 0 mas elementos pertenecientes a un conjunto. Como,
ejemploconsiderense las listas de alumnos matriculados en una cierta asig-
natura, las listas del censo, los indices alfabeticos de los libros, las guias
telef6nicas, etc. Esto quiere decir que much os problemas estan relaciona-
dosde alguna forma con el proceso de ordenaci6n. Es por 10 que la orde-
naci6n es un problema importante a considerar.
La ordenaci6n, tanto numerica como alfanumerica, sigue las mismas
reglasque empleamos nosotros en la vida normal. Esto es, un dato nume-
rico es mayor que otro, cuando su valor es mas grande, y una cadena de
caractereses mayor que otra, cuando por orden alfabetico esta despues.
Podemos agrupar los metodos de ordenaci6n en dos categorias: orde-
naci6nde arrays u ordenaci6n interna, cuando los datos se guardan en me-
moria interna, y ordenaci6n de ficheros u ordenaci6n externa, cuando los
datos se guardan en memoria externa, generalmente discos.
En este capitulo no tratamos de analizar exhaustivamente todos los
metodosde ordenaci6n y ver sus prestaciones de eficiencia, rapidez, etc.;
nosvamosa centrar en los metodos mas comunes para ordenaci6n de arrays
y de ficheros.
Haymuchas formas de clasificar datos y una de las mas conocidas es la
clasificaci6npor el metodo de la burbuja.
Veamosa continuaci6n el algoritmo correspondiente, para ordenar una
listade menor a mayor, partiendo de que los datos a ordenar estan en una
listade n elementos:
1.- Comparamos el primer elemento con el segundo, el segundo con
el tercero, el tercero con el cuarto, etc. Cuando el resultado deuna
comparaci6n sea "mayor que", se intercambian los valores de10s
elementos comparados. Con esto conseguimos llevar el valor ma-
yor a la posici6n o.
2.- Repetimos el punta 1, ahora para los 0-1 primeros elementos de
la lista. Con esto conseguimos llevar el valor mayor de estos ala
posici6n 0-1.
3.- Repetimos el punta 1, ahora para los 0-2 primeros elementos de
la lista y as! sucesivamente.
4.- El proceso termina despues de repetir el punta 1,0-1 veces,0 cuando
al finalizar la ejecuci6n del punta 1no haya habido ningun cambio.
<junci6n clasijicar(array "a" de "n" elementos) >
["a" es un array cuyos elementos son aD' aI' ..., an_i
n = n-l
DO WHILE ("a" no este ordenado y n > 0)
i = 1
DO WHILE (i < = n)
IF ( a[i-l] > alii ) THEN
permutar a[i-l] con a[i]
ENDIF
i = i+l
ENDDO
n = n-l
ENDDO
END <clasijicar >
El siguiente ejemplo presenta la programaci6n de este algoritmo para
el caso concreto de ordenar alfabeticamente una lista de cadenas de ca-
racteres.
# include <stdio.h>
# include <stdlib.h>
iot LeerLineas(char **, iot);
void Clasijicar(char **, iot);
void EscribirLineas(char **, iot);
main( )
[
char **lineas;
iot lineasmax;
iot nlineas;
/ * puntero al array que contiene las lfneas */
/ * mimero maximo de lfneas a ordenar */
/ * mimero de lfneas lefdas */
printj(Hn 0 maximo de lfneas a ordenar: ");
scanj(H%d': &lineasmax);
/ *Asignaci6n de memoria para lineas[lineasmax][MAXC] */
if (!(lineas = (char **)malloc(sizeof(char) *lineasmax*MAXC)))
[
perror(Herror: insujiciente espacio de memoria  n");
exit(l);
1
system(Hcls");
printf(HProceso de clasijicaci6n de lfneas de caracteres.  n ");
printj(HCada lfnea jinaliza con Enter.  n  n ");
printjt'Entrada de lfneas. AZ para jinalizar.  n");
if ((nlineas = LeerLineas(lineas, lineasmax)) > = 0)
[
printj("Proceso de clasijicaci6n.  n  n");
Clasijicar(lineas, nlineas);
EscribirLineas(lineas, nlineas);
1
else
printjt'Demasiadas lfneas para clasijicar.  n");
iot LeerLineas(char **lineas, iot lineasmax)
{
iot nlineas,o
char *p,o
nlineas = -I;
/ * leer n lineas */
while ((p = gets(lineas[+ +nlineasJ)) /= NULL)
{
if (nlineas > lineasmax)
return (-i); / * demasiadas lineas para ordenar */
}
return (nlineas),o
void Clasijicar(char **lineas, iot numero_de_lineas)
{
char aux[MAXCj,o
iot i, s,o
s = i,o
while ((s i) && (--numero_de_lineas > 0))
{
s = 0; / * no permutaci6n */
for (i = i,oi < = numero_de_lineas,o i+ +)
/ * i la linea (i-i) es mayor que la linea (i) ? */
if (strcmp(lineas[i-ij, lineas[i}) > 0)
(
/ *permutar las lineas (i-i) e (i) */
strcpy(aux, lineas[i-ij),o
strcpy(lineas[i-ij, lineas[i}),o
strcpy(lineas[i}, aux),o
s = i,o / * permutaci6n */
void EscribirLineas(cha'r **lineas, iot nlineas)
!
while (nlineas-- > 0)
printf("%s  n': lineas[i+ +J);
stoma el valor 1 cuando, al menos, se efectua un cambio entre dos ele-
mentos. Si en una exploraci6n a 10 largo de la lista, no se efectua cambio
alguno, s permaneceni valiendo 0, 10 que indica que la lista esta ordenada,
terminando as! el proceso.
Cuando se analiza un metoda de ordenaci6n, hay que determinar cuan-
tas comparaciones e intercambios se realizan para el caso mas favorable,
para el caso medio y para el caso mas desfavorable.
En el metoda de la burbuja, en el caso mas desfavorable se realizan
(0-1)(0/2)= (02-0)/2 comparaciones, donde 0 es el numero de elementos
a ordenar. El numero de intercambios es 0 para el caso mas favorable (lista
ordenada), 3(02-0)/4 para el caso medio y 3(02-0)/2 para el caso menos
favorable(hay tres intercambios por cada elemento desordenado). El ana-
lisismatematico que conduce a estos valores, queda fuera del prop6sito
de este libro. El tiempo de ejecuci6n es un multiplo de 02 y esta directa-
mente relacionado con el numero de comparaciones y de intercambios.
El algoritmo para este metodo de ordenaci6n es el siguiente: inicialmente,
seordenan los dos primeros elementos del array, luego se inserta el tercer
elementoen la posici6n correcta con respecto a los dos primeros, a conti-
nuaci6nse inserta el cuarto elemento en la posici6n correcta con respecto
a lostres primeros elementos ya clasificados y as! sucesivamente hasta lle-
gar al ultimo elemento del array.
478 ENCICLOPEDIA DEL LENGUAJE C
Ejemplo:
Valores iniciales: 46 54 12 30 84 18 10 77
T
46 54 12 30 84 18 10 77
1
T
12 46 54 30 84 18 10 77
, T
12 30 46 54 84 18 10 77
T
12 30 46 54 84 18 10 77
"
T
12 18 30 46 54 84 10 77
;: T
10 12 18 30 46 54 84 77
,J
Valores ordenados: 10 12 18 30 46 54 77 84
El pseudocodigo para este algoritmo puede ser el siguiente:
<juncion insercion(array <fa"de <fn" elementos) >
[<fa" es un array cuyos elementos son aO'aI' ..., an-i
i = 1
DO WHILE ( i < = n)
x = alii
insertar x en la posicion correcta entre ao y ai
ENDDO
END <insercion >
La programacion de este algoritmo, para el caso concreto de ordenar
numericamente una lista de numeros, es la siguiente:
# include <stdio.h >
# include <std/ib.h>
int /ista[ I = { 46, 54, 12, 30, 84, 18, 10, 77};
int n_elementos = sizeof(/ista)/sizeof(int);
main()
!
printf(HLista ordenada:  n ");
for (i = 0; i < n~elementos; i+ +)
printf(H%6d': lista[i]);
void insercion(int lista[ ], int n_elementos)
!
/ * desde el segundo elemento */
for (i = 1; i < n_elementos; i+ +)
{
x = lista[i];
k = i-I;
while (k > =0 && x < lista[k])
{
lista[k+1] = lista[k]
k--;
J
lista[k+I] = x;
/*para k=-I, se ha alcanzado*/
/ * el extremo izquierdo. */
/ * x es menor que lista[k] */
n-l
(n2+n-2)/4
(n2+n)/2-1
caso medio
caso menos favorable
2(n-l)
(n2+9n-1O)/4
(n2 + 3n-4)/2
Para el metoda de inserci6n, el tiempo de ej.ecuci6n es funcion de
02 y esta directamente relacionado con el numero de comparaciones y de
intercambios.
El metoda de ordenaci6n Quicksort, esta generalmente considerado como
el mejor algoritmo de ordenaci6n disponible actual mente.
1.- Se selecciona un valor del array. Este valor se puede escoger aleatoria·
mente 0 haciendo la media de un pequeno conjunto de valores torna-
dos del array. El valor 6ptimo seria aquel que este precisamente enme-
dio del rango de valores (mediana). No obstante, incluso en el peorde
los casos (el valor escogido esta en un extremo), Quicksort funciona
correctamente.
2.- Se divide el array en dos partes, una con todos los elementos menores
que el valor seleccionado y otra con todos los elementos mayores0
iguales.
3.- Se repiten los puntos 1 y 2 para cada parte restante hasta que el array
este ordenado.
<!unci6n qs(array "a") >
Se elige un valor x
DO WHILE ( "a" no este ordenado )
[dividir "a" en dos partes: a_in! y .a~upJ
a_in! con los elementos ai < x
a~up con los elementos ai > = x
ENDDO
IF ( existe a_in!) THEN
qs( a_in!)
ENDIF
IF ( existe a-----sup) THEN
qs( a-----sup)
ENDIF
END <qs>
A eontinuacion se muestra una version de este algoritmo, el eual se-
leeeiona el elemento medio del array, 10 eual resulta faeil de implementar,
aunque no siempre da lugar a una buena eleeeion. A pesar de ella, funcio-
na eorreetamente.
# include <stdio.h>
# include <std!ib.h>
int !ista!] = { 10, 3, 7, 5, 12, 1, 27, 3, 8, 13 };
int n_elementos = sizeof(!ista)/sizeof(int);
main( )
!
printj(HLista ordenada:  n");
for (i = 0; i < n_elementos; i+ +)
printj(H%6d': !ista!i]);
void quicksort(int !ista! ], int n_elementos)
!
'",.# )
ri'  ,I'"
/ * Funci6n recursiva qs */" y u
void qs(iot lista[ J, iot inf, iot sup)
{
register izq, der;
iot mitad, x;
U ~ (O}
izq = inf,oder sup;
mitad = lista[(izq+der)/2J; 0
do 1- ,1'1'
"{ It I
while (lista[izqJ < mitad && izq < sup) izq+ +;
while (mitad < lista[derJ && der > inf) der--;
if (izq < = der) 0 l ",t<"'
{~ J 'f'
x = lista[izqJ, lista[izqJ lista[derJ, lista[derJ = x;
izq+ +; der--;
} 1,
} )b ~ i
while (izq < = der); .
if (i if <pder)qs(lista, inf, der);
, .:>'
if (izq < sup) qs(lista, izq, sup);
} ~ f p~
')';1
Amilisis del metoda quicksort: en el caso mas favorable en que, cada
vez, se se1ecciona la mediana obteniendose dos particiones iguales, se rea-
lizan o.log 0 comparaciones y 0/6.1og 0 intercambios, donde 0 es el mime-
ro de elementos a ordenar; en el caso medio el rendimiento es inferior aI
caso optimo en un factor de 2.log 2; y en el caso menos favorable en que,
cada vez, se selecciona el valor mayor obteniendose una particion de 0-1
elementos y otra de 1elemento, el rendimiento es del orden de 0.0=02,
Con el fin de mejorar el caso menos favorable, se sugiere elegir, cad a vez,
un valor aleatoriamente 0 un valor que sea la median a de un pequeno con·
junto de valores tornados del array.
La funcion qs sin utilizar la recursion puede desarrollarse de la forma
siguiente:
void quicksort(i.ot lista[ J, iot n_elementos)
[
qs(lista, 0, n_elementos - 1);
1
/ * Funcion no recursiva qs */
void qs(i.ot lista[ J, iot inj, iot sup)
[
# define NE 100
struct .elemento-pila
!
iot inj, sup;
1 pila[NEJ;
register izq, der;
iot mitad, x, p;
/ * Inicializar la pila con los valores: inj, sup */
p = 1, pila[pJ.inf = inj, pila[pJ.sup = sup;
do
!
/ * tomar los datos inj, sup de la parte superior de la pila */
inf = pila[pJ.inj, sup = pila[pJ.sup, p--;
do
! / * Division del array en dos partes */
izq = inj,' der = sup;
mitad = lista[(izq+der)/2J;
do
!
while (lista[izqJ < mitad && izq < sup) izq+ +;
while (mitad < lista[derJ && der > inf) der--;
if (izq < = der)
!
x= lista[izqJ, lista[izqJ= lista[derJ, lista[derJ=x;
izq + +,o der--,o
1
1
while (izq < = der),o
if (izq < sup)
{ / * meter en la pi/a los valores: izq, sup */
P+ +, pi/a[p].inf = izq, pi/a[p}.sup = sup;
J
/ * inf = inf; */ sup = der;
J
while (inf < der);
J
while (p);
J
En esta soluci6n obs~rvamos que despues de cada paso se generan do'
nuevas sublistas. Una de ellas la tratamos en la siguiente iteraci6n y la otra
la posponemos, guardando sus limites inf y sup en una pila.
La tabla siguiente muestra los tiempos, en segundos, consumidos por 10
metodos de ordenaci6n estudiados anteriormente (sistema con micropro·
cesador 80386SX y compilador Microsoft C 6). Los tiempos corresponden
a la ordenaci6n de un array de 1000 elementos de tipo float.
tiempo para ordenar un array que inicialmente ya esta or·
denado.
tiempo para ordenar un array que inicialmente esta ordena
do en senti do inverso.
tiempo para ordenar un array que inicialmente ya esta or-
denado, pero, al que se Ie han permutado aleatoriamente dos
de sus elementos.
El metoda de la burbuja es el peor de los metodos; el metoda de in-
serci6n directa mejora considerablemente y el metoda quicksort es el mas
nipido y mejor metoda de ordenaci6n de arrays con diferencia.
El objetivo de ordenar un conjunto de objetos, generalmente es facilitar
la busqueda de uno 0 mas elementos pertenecientes a ese conjunto; aun-
que es posible realizar dicha busqu~>dasin que el conjunto de objetos este
ordenado, pero esto trae como consecuencia un mayor tiempo de proceso.
Este metoda de busqueda, aunque valido, es el menos eficiente. Se basa
en comparar el valor que se desea buscar con cada uno de los valores del
array. El array no tiene por que estar clasificado.
<funci6n Btisqueda--S( array a, valor que queremos buscar) >
i = 0
DO WHILE ( no encontrado )
IF (valor = alii)
encontrado
ENDIF
i = i+l
ENDDO
END <Btisqueda--S >
Como ejercicio, escribir el c6digo correspondiente a un programa que
permita buscar un valor en un array previamente leido.
Un metodo eficiente de busqueda, que puede aplicarse a los arrays c1asifi·
cados, es la busqueda binaria. Partiendo de que los elementos del arra}
estan almacenados en orden ascendente, la busqueda binaria consisteen
10 siguiente:
Se selecciona el elemento del centro 0 aproximadamente del centrodel
array. Si el valor a buscar no coincide con el elemento seleccionado y e5
mayor que el, se continua la busqueda en la segunda mitad del array.Si,
por el contrario, el valor a buscar es menor que el valor del elemento selec-
cionado, la busqueda continua en la primera mitad del array. En ambos
casos, se halla de nuevo el elemento central, correspondiente al nuevoin·
tervalo de busqueda, repitiendose el cicIo. El proceso se repite hasta que
se encuentra el valor a buscar, 0 bien hasta que el intervalo de busqueda
sea nulo, 10 que querra decir que el elemento buscado no figura en el array.
<funcion Blisqueda-B( array a, valor que queremos buscar ) >
DO WHILE ( exista un intervalo donde buscar y no encontrado )
x = elemento mitad del intervalo de blisqueda
IF (valor = x) THEN
encontrado
ELSE
IF (valor > x) THEN
buscar "x" en la segunda mitad del intervalo de blisqueda
ENDIF
IF (valor < x) THEN
buscar "x" en la prim era mitad del intervalo de blisqueda
ENDIF
ENDIF
ENDDO
END <Blisqueda-B >
# include <stdio.h >
# include <stdlib.h >
float lista[ I = { 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33,
36, 39, 42, 45, 48, 51, 54, 57, 60 };
main( )
(
int posicion;
float valor;
printf(Hlntroducir el valor a buscar ");
scanJtt%r: &valor);
if (posicion / = -1)
ptintj(H  nLa posicion de %g es %d  n': valor, posicion);
/ * La Juncion devuelve como resultado la posicion del valor.
* Si el valor no se localiza se devuelve un resultado -1.
*/
int BusquedaB(f1oat lista[ I, float v, int inj, int sup)
{
int mitad;
/ * Comprobar si la blisqueda Jalla */
if ((inJ > = sup) && (lista[inf] /= v))
(
printj(HBlisqueda sin exito  n");
return -1;
J
/ * Sigue fa bLisqueda */
mitad = (inj + sup) / 2;
if (lista[mitad] = = v) .
(
printj("  nBLisqueda con exito  n");
printf("Ef primer efemento ocupa fa posicion 0  n");
return mitad;
}
else
if (v > lista[mitadJ)
BusquedaB(lista, v, mitad + 1, sup);
else
BusquedaB(lista, v, in/, mitad - 1);
Cuando el fichero es pequeno, esto es si tiene pocos registros, se puede co-
piar en memoria en un array y utilizando las tecnicas vistas anteriormente,
ordenar dicho array, copiando a continuaci6n el array ordenado en el fi-
chero. Sin embargo, muchos ficheros son demasiado grandes para tratar·
los de esta forma, por 10 que para ordenarlos recurriremos a tecnicas espe-
ciales.
EI siguiente programa desarrolla un algoritmo de ordenaci6n de un fiche-
ro secuencial, denominado mezcla natural. La secuencia inicial de elemen-
tos, viene dada en e1fichero c y se utilizan dos ficheros auxiliares denomi-
nados a y b. Cada pasada consiste en una fase de distribucion que reparte
equitativamente los tramos ordenados del fichero c sobre los ficheros ay
b, y una fase que mezcla los tramos de los ficheros a y b sobre el ficheroc.
b b
'--~ '--2- fose de mezclo
----------.: fo':.e de dl':.~rtbuClon
'--~
posada n
Partimos de un fichero c. Con el fin de ilustrar el metoda de mezcla natu-
ral, separemos los tram os ordenados en el mismo, por un gui6n ( - ).
fichero a: 18 32 - 14 42 44 68
fichero b: 10 60 - 12 24 30 48
fichero a: 10 18 32 60
fichero b: 12 14 24 30 42 44 48 68
Observese que s610 se necesitan dos pasadas. El proceso finaliza, tan
pronto como el numero de tramos ordenados del fichero c, sea 1.
Una forma de reducir el numero de pasadas es distribuir los tramos
ordenados sobre mas de dos ficheros.
Este programa sugiere un nuevo ejercicio, que se deja para que ellec·
tor 10 resuelva, cuyo enunciado es: dados dos ficheros ordenados a y b,
obtener como resultado un fichero c tambien ordenado, que sea fusionde
10s dos ficheros anteriores.
# include <stdio.h>
# include <stdlib.h>
const int FALSE = 0;
const int TRUE = 1;
void listar(FILE *pjx);
void mezcla-'laturat(void);
void distribucion(void);
void copiar_tram 0(FILE *pjx, FILE *pjy);
void copiar(FILE *pjx, FILE *pjy);
void mezcla(void);
void mezcla_tramo(void);
typedef struct datos registro;
struct datos
{
long clave;
/ * otros campos */
} reg;
size_t t----.reg= sizeof(registro);
/ * tipo registro */
/ * dejinicion de un registro */
/ * registro */
/ * tamano de un registro */
FILE *pjc;
FILE *pja;
FILE *pjb;
/ * puntero at jichero c */
/ * puntero at jichero a */
/ * puntero at jichero b */
int n_tramos;
int jin_de_tramo;
main( )
{
char sclave[10};
/ * Abrir el jichero c para leer/escribir */
pjc = jopen("c': "w+b");
sjstem("cls' ');
printj ("Pulse A Z para jinalizar  n  n");
printj ("Clave............ ");
while (gets(sclave) /= NULL)
!
reg.clave = atol(sclave);
/ * se leen el resto de los campos */
jwrite (&reg, t-,"eg, 1, pjc);
printj ("Clave............ ");
I
listar(pjc);
mezcla_natural( );
listar(pjc);
jclose(pjc);
l
/ * Visualizar todos los registros de un jichero */
void listar(FILE *pjx)
(
system("cls ");
rewind(pjx); / *posicionarse al principio del jichero */
/ * Leer el primer registro del jichero */
jread (&reg, t_reg, 1, pjx);
while (lfeof(pjx))
!
printf("%d ': reg.clave);
/ * escribir el resto de los campos */
/ * Leer el siguiente registro del jichero */
jread (&reg, t_reg, 1, pjx);
/ * Algoritmo de ordenaci6n, mezcla natural */
void mezcla_natural(void)
{
do
(
/ * Crear y abrir los jicheros temporales a y b */
pja = tmpjile();
pfb = tmpjile();
rewind(pjc);
distribucion( );
n_tramos = 0;
rewind(pjc); rewind(pfb); rewind(pja);
mezcla( );
rmtmp( ); / * borrar jicheros temporales */
J
while (n_tramos != 1);
J
/ * Repartir equitativamente los tramos ordenados de c en a y b */
void distribucion(void) / * desde c hacia a y b */
{
do
(
copiar_tramo(pjc, pja);
if (!feof(pjc)) copiar_tramo(pjc, pfb);
J
while (!feof(pjc));
J
/ * copiar un tramo de x a y */
void copiar_tramo(FILE *pjx, FILE *pjy)
{
do
(
J
while (!fin_de_tramo);
J
void copiar(FILE *pjx, FILE *pjy)
{
long posicion;
registro regx;
jread (&reg, t-,eg, 1, pjx);
if (feof(pjx))
jin_de~tramo = TRUE;
else
{
jwrite (&reg, t-,eg, 1, pjy);
/ * Obtener el siguiente registro de x; verificar si se ha
* lIegado al jinal de un tramo; recuperar la posicion.
*/
posicion = jtell(pjx);
jread (&regx, t-,eg, 1, pjx);
if (feof(pjx))
jin_de_tramo = TR UE;
else
(
jseek(pjx, posicion, SEEK--.SET);
jin_de_tramo = reg.c1ave > regx.c1ave;
J
}
J
/ * Mezclar tramos de jicheros a y b, ordenadamente sobre c */
void mezcla(void) / * desde a y b hacia c */
(
while (ljeof(pja) && Ijeof(pjb))
(
mezcla_tramo( );
n_tramos + = 1;
J
/ * copiar el resto de los tramos del jichero no jinalizado */
while (ljeof(pja))
(
copiar_tramo(pja, pjc);
n_tramos + = 1;
J
while (ljeoj(pjb))
(
copiar_tramo(pjb, pjc);
n_tramos + = 1;
J
J
/ * intercalar un tramo de a y otro de b ordenadamente d
void mezcla_tramo(void)
{
long posicion_a, posicion_b;
registro rega, regb;
do
(
/ * Obtener el siguiente registro de a y b; recuperar la
* posicion; copiar ordenadamente en c.
*/
posicion_a = jtell(pja); posicion_b = jtell(pfb);
jread (&rega, t-,eg, 1, pja);
jread (&regb, t-,eg, 1, pfb);
jseek(pja, posicion_a, SEEK_SET);
jseek(pfb, posicion_b, SEEK~ET);
if (rega.clave < = regb.clave)
(
copiar(pja, pjc);
if (fin_de_tramo) / * copiar el resto del tramo de b */
copiar_tramo(pfb, pjc);
I
else
(
copiar(pfb, pjc);
if (fin_de_tramo)
copiar_tramo(pja, pjc);
I
}
while (fjin_de_tramo);
I
Los ficheros de acceso aleatorio, a diferencia de 10s ficheros que solo pue-
den ser accedidos secuencia1mente, permiten actua1izar 1a informacion sin
tener que copiarla sobre otro fichero y pueden tratarse de forma amiloga
a 10s arrays, 10 que simp1ifica enormemente 1a ordenacion de 10s mismos.
Esto quiere decir que los metodos expuestos para ordenar arrays, pueden
ser aplicados tambien para ordenar ficheros que pueden ser accedidos alea-
toriamente.
El siguiente programa ordena un fichero, en el cual cada registro esta
formado por dos campos: referencia y precio. El desarrollo del programa
variara en funci6n de la estructura de los datos y del tipo del campo (nu-
merico 0 alfanumerico) que se utilice para la ordenaci6n del fichero. No-
sotros vamos a ordenar el fichero por el campo referencia, de tipo alfabeti-
co, empleando el metoda Quicksort explicado anteriormente.
/ * Metodo de ordenacion Quicksort para jicheros
* accedidos aleatoriamente
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
typedef struct datos registro;
struct datos
{
char rejerencia[20};
long precio;
I;
registro reg;
int t~eg = sizeof(registro);
FILE *pj;
/ * tipo registro */
/ * dejinicion de un registro */
/ * registro */
/ * tamano de un registro */
/ *puntero al jichero */
void permutar ---fegistros(FILE *p/' int izq, int der);
char *Campo(FILE *p/' int n);
main( )
{
register i;
int n_elementos;
if ((pj = jopen("datos': "r+b")) = = NULL)
{
printj("EI jichero  "datos " no puede abrirse  n");
exit(l);
1
system(Ccls");
jseek(pj, OL, SEEK--.END);
n_elementos = jtell(pj)/t-feg;
rewind(pf);
quicksort(pj, n_elementos);
printj("Fichero ordenado  n");
jclose(pf);
1
void qs(FILE *pj, int inj, int sup);
void quicksort(FILE *pj, int n_elementos)
{
qs(pj, 0, n_elementos - I);
1
void qs(FILE *pj, int inj, int sup)
{
register izq, der;
char *mitad;
izq = inj,· der = sup;
/ * Obtener el campo mitad por el que se va a ordenar,
* del registro mitad
*/
strcpy(mitad, campo(pj, (int)(izq+der)/2));
do
{
while (strcmp(campo(pj,izq), mitad) < 0 && izq < sup) izq+ +;
while (strcmp(mitad, campo(pj,der)) < 0 && der > inf) der--;
if (izq < = der)
{
permutar -fegistros(pj, izq, der);
izq + +; der--;
l
l
while (izq < = der);
if (inf < der) qs(pj, inj, der);
if (izq < sup) qs(pj, izq, sup);
l
/ * Permutar los registros de las posiciones izq y der */
void permutar -.registros(FILE *pj, iot izq, iot der)
I
registro x, y;
jseek(pj, (loog)izq * t_reg, SEEK_SET);
jread(&x, t_reg, 1, pf);
jseek(pj, (loog)der * t_reg, SEEK~ET);
jread(&y, t_reg, 1, pf);
jseek(pj, (loog)izq * t-.reg, SEEK_SET);
jwrite(&y, t-.reg, 1, pf);
jseek(pj, (loog)der * t_reg, SEEK_SET);
jwrite(&x, t-.reg, 1, pf);
l
/ * Leer el campo utilizado para ordenar */
char *Campo(FILE *pj, iot n)
I
jseek(pj, (loog)n * t_reg, SEEK_SET);
jread(&reg, t-feg, 1, pf);
return (reg.referencia);
l
Losalgoritmos hash son metodos de busqueda, que proporcionan una "100-
gitud de busqueda" pequefia y una flexibilidad superior a la de otros me-
todos, como puede ser, el metodo de "busqueda binaria" que requiere que
los elementos del array esten ordenados.
Por "Iongitud de busqueda" se entiende el numero de accesos quees
necesario efectuar sobre un array para encontrar el elemento deseado.
Este metoda de busqueda permite, como operaciones basicas, ademas
de la busqueda de un elemento, insertar un nuevo elemento (si el array esta
vacio, crearlo) y eliminar un elemento existente.
Un array producto de la aplicaci6n de un algoritmo hash se denomina "array
hash" y son los arrays que se utilizan con mayor frecuencia en los sistemas
de acceso. Graficamente estos arrays tienen la siguiente forma:
El array se organiza con elementos formados por dos miembros: clave
y contenido.
La clave constituye el medio de acceso al array. Aplicando a la clave
una funci6n de acceso "fa", previamente definida, obtenemos un numero
entero positivo "i", que nos da la posici6n del elemento correspondiente,
dentro del array.
Conociendo la posici6n, tenemos acceso al contenido. El miembro con·
tenido puede albergar directamente la informaci6n, 0 bien un punteroa
dicha informaci6n, cuando esta sea muy extensa.
El acceso, tal cual 10 hemos definido, recibe el nombre de "acceso
directo".
Como ejemplo, suponer que la clave de acceso se corresponde con el
numero del documento nacional de identidad (dni) y que el contenido son
los datos correspondientes a la persona que tiene ese dnL Una funci6n de
acceso, i = fa(dni), que haga corresponder la posici6n del elemento en el
array con el dni, es inmediata:
la cual da lugar a un acceso directo. Esta funci6n as! definida presenta un
inconveniente y es que el numero de val ores posibles de "i" es demasiado
grande para utilizar un array de este tipo.
Para solucionar este problema, es posible siempre, dado un array de
longitud L, crear una "funcion de acceso", fa, de forma que nos genere
un valor comprendido entre 0 y L, mas comunmente entre 1 y L.
En este caso puede suceder que dos 0 mas claves den un mismo valor
de "i":
EI metoda hash esta basado en esta tecnica; el acceso al array es direc-
to por el numero "i" y cuando se produce una colision (dos claves diferen-
tes dan un mismo numero "i") este elemento se busca en una zona deno-
minada "area de overflow".
Este es uno de los metodos mas utilizados. El algoritmo para acceder a
un elemento del array de longitud L, es el siguiente:
2. Si la posici6n "i" del array est a libre, se inserta la clave y el conte-
nido. Si no est a libre y la clave es la misma, error: clave duplica-
da. Si no esta libre y la clave es diferente, incrementamos "i" en
una unidad y repetimos el proceso descrito en este punta 2.
5040
3721
4007
3900
6375
En la figura observamos que queremos insertar la clave 6383. Supon·
gamos que aplicando la funci6n de acceso obtenemos un valor de 3, estoes,
Como la posici6n 3 esta ocupada y la clave es diferente, tenemos que incre-
mentar "i" y volver de nuevo al punta 2 del algoritmo.
La "Iongitud media de btisqueda" en un "array hash abierto" viene
dada por la expresi6n:
Si existen 60 elementos en un array de longitud L = 100,el numero me-
dio de accesos para localizar un elemento sera:
En el metoda de "busqueda binaria", el numero de accesos viene dado
por logz N, siendo N el numero de elementos del array.
Para reducir al maximo el numero de colisiones y, como consecuen-
cia, obtener una "Iongitud media de busqueda" baja, es importante elegir
bien la funcion de acceso.
Una "funcion de acceso" 0 "funcion hash" bastante utilizada y que
proporciona una distribucion de las claves uniforme y aleatoria es la "fun-
cion mitad del cuadrado" que dice: "dada una clave C, se eleva al cuadra-
do (CZ) y se cogen n bits del medio, siendo 2" < = L.
Supongamos: L 256 10 que implica n = 8
C 625
CZ 390625 ( 0 < = CZ < = 2Rl )
39062510 00000000000001011111010111100001Z
n bits del medio: 01011111z= 9510
Otra "funcion de acceso" muy utilizada es la "funcion modulo" (resto
de una division entera):
Cuando se utilice esta funcion es importante elegir un numero primo
para L, con la finalidad de que el numero de colisiones sea pequeno.
Una alternativa al metoda anterior es la de disponer de otro array separa-
do, para insertar las claves que producen coli.sion, denominado "array de
overflow", en el cual se almacenan todas estas claves de forma consecutiva.
CLAVE CONTENIDO
5040
3722
2039
6383
Otra forma alternativa mas normal es organizar una lista encadenada
por cada posicion del array donde se produzca una colision.
5040
3722 • -I
4007 • -I
2039 I [3-...
6383 I
Cada elemento de esta estructura incorpora un nuevo miembro P, el
cual es un puntero a la lista encadenada de overflow.
En el metodo hash la eliminacion de un elemento no es tan simple como
dejar vado dicho elemento, ya que esto da lugar a que los elementos inseT-
tados por colision no puedan ser accedidos. Por ello se suele utilizar un
miembro (campo) complementario que sirva para poner una marca de que
dicho elemento esta eliminado. Esto permite acceder a otros elementos que
depend en de el por colisiones, ya que la clave se conserva y tambien permi-
te insertar un nuevo elemento en esa posicion cuando se de una nueva co-
lision.
Crear un array hash de una determinada 10ngitud L que permita almace-
nar los datos numero de matricula y nombre de 10salumnos matriculados
en una cierta Universidad, utilizando el metoda hash abierto y la funci6n
de acceso modulo.
<juncion hash(array, n_elementos, elemento x) >
[El array estci iniciafizado a valor OJ
i = matricula modulo n_elementos
DO WHILE (no insertado y haya elementos fibres)
IF (elemento Hi" estci fibre) THEN
copiar elemento x en la posicion i
ELSE
IF (clave dupficada) THEN
error: clave dupficada
ELSE
[se ha producido una cofisionJ
[avanzar al siguiente elementoJ
i = i+l
IF (i = n_elemento) THEN
i = 0
ENDIF
ENDIF
ENDIF
ENDDO
END <hash>
# include <stdio.h>
# include <stdfib.h>
# include < math.h >
# include <string.h>
struct datos
[
unsigned int matricula;
char mombre;
IIIIIIIIIIIIIIII rutina de manipulacion del error 111111111///11/
void error(void)
[
perror((error: insuficiente espacio de memoria ");
exit(l);
void hash(elemento *, int, elemento);
int siguiente-primo(int);
main( )
[
elemento *a;
int n_elementos;
int i;
char nom[81];
elemento x;
1* direccion de comienzo del array.
1* nOde elementos del array.
printj((n 0 de elementos: ");
scanft' %d': &n_elementos);
n_elementos = siguiente-primo(n_elementos);
I * crear el array dinamico «a" *1
a = (elemento *)calloc(n_elementos, sizeof(elemento));
if (Ia) error( );
1* Introducir datos *1
printj((Introducir datos. Finalizar con matrfcula
printj((motrirula: ");
1* Inicializar el array *1
for (i= 0; i < n_elementos; i+ +)
[
a[i].matricula = 0;
a[i].nombre = NULL;
scanj("%u'~ &x.matricula); jjlush(stdin);
while (x.matricula)
(
printj("nombre: "); gets(nom);
/ * asignar espacio para nombre */
x.nombre = (char *)malloc(strlen(nom) +1);
if (!x.nombre) error( );
strcpy (x.nombre, nom);
hash(a, n_elementos, x); / * llamada a la junci6n hash */
printj("matrlcula: ");
scanf("%u'~ &x.matricula); jjlush(stdin);
void hash(elemento ~, int n_elementos, elemento x)
{
iot i; / * lndice para rejerenciar un elemento */
iot conta = 0, insertado = 0; / * contador */
i = x.matricula % n_elementos; / *junci6n de acceso */
while (linsertado && conta < n_elementos)
[
if (a[i].matricula = = 0)
(
a[i] = x;
insertado
)
else
it (x.matricula = = a[i].matricula)
(
printf("error: matrlcula duplicada  n");
insertado = 1;
)
else
{
/ * Siguiente elemento libre */
i+ +, conta+ +;
if (i = = n_elementos) i = 0;
if (conta = = n_elementos)
printft'error: array !lena  n");
/ / / / / / Buscar un ntimero primo a partir de un mimero dado / / / / //
int siguiente-primo(int n)
{
if (n % 2 0)
n++;
while (!primo)
{
primo = 1;
for (i = 3; i < = (int)sqrt((double)n); i + = 2)
if (n % i = = 0)
primo = 0;
if (!primo) n + = 2;
/ * no primo */
/ * siguiente impar */
5
Tecnicas Avanzadas
• Manejo de la Memoria
• Compilar y Enlazar
• Librerias y Utilidades del Compilador
• Rutinas en Lenguaje Ensamblador
• Comunicaciones. Servicios del DOS y del BIOS
• C y DOS
• Control de Procesos
DOS carga e1 c6digo y 10s datos correspondientes a un programa en seg-
mentos en la memoria fisica (RAM). Cad a segmento es de un tamano de
64K. El numero minimo de segmentos asignados a un programa es dos,
ya que e1c6digo y 10s datos se asignan en segmentos separados. De 10s mo-
delos existentes, 10s mode10s de memoria pequenito (tiny) y pequeno (small)
utilizan s01amente dos segmentos, otros mode10s de memoria, discutidos
a continuaci6n, utilizan mas segmentos.
El mode10 utilizado por defecto por e1 compilador Microsoft C es el
modelo pequeno (small). Este puede cambiarse desde e1 entorno PWB 0
desde la orden de compi1ar emitida bajo e1 sistema operativo.
Si e1 programa tiene mas de 64K de c6digo 0 mas de 64K de datos
asignados estaticamente, uti1izaremos un mode10 de memoria diferente a1
modelo pequeno (small).
1. Compilar con 1a orden CL uti1izando 1a opci6n /A para especifi-
car el mode10 de memoria adecuado: pequenito (tiny), pequeno
(small), medio (medium), compacta (cQmpact), grande (large),0
enorme (huge).
2. Crear un modele mixto utilizando las palabras clave_near, -Jar,
o _based.
Una de las caracteristicas mas importantes dellenguaje C es que permite
utilizar punteros para acceder directamente a la memoria.
Cada programa C tiene al menos dos partes: el c6digo (definiciones
de funciones) y los datos (variables y constantes). Cuando un programa
se ejecuta, tanto el c6digo como los datos son referenciados por sus direc-
ciones. Estas direcciones pueden almacenarse en variables declaradas como
punteros. El tamafio de estas variables puede ser de 16 0 de 32 bits; esto
depende del segmento de memoria donde se localicen los elementos refe-
renciados.
Los ordenadores que utilizan la familia de microprocesadores 80x86deIn-
tel, gestionan la memoria en segmentos de 64K. Por 10tanto, para referen-
ciar un elemento se necesita conocer, la direcci6n base del segmento y el
desplazamiento dentro del segmento.
Ellimite de los 64K es necesario porque los registros de la familia 80x86
son de 16bits; esto quiere decir, que un registro s610puede direccionar 65536
posiciones de memoria (64K).
Segun 10expuesto, una variable de tipo puntero que especifique cual-
quier posici6n de memoria, necesita 16 bits para la direcci6n base del seg-
mento y otros 16 bits para el desplazamiento dentro del segmento. Enton-
ces necesita un total 32 bits.
En la familia 80x86 el registro CS contiene la direcci6n base del seg-
mento de c6digo; el registro DS contiene la direcci6n base del segmento
dedatos; el registro SS la direcci6n base del segmento de la pila; y el regis-
tro ES la direcci6n base del segmento extra. El microprocesador 80386, tiene
dos registros adicionales para direccionar otros segmentos: FS yES.
Microsoft C utiliza por defecto el modele de memoria pequeno (small),
quepermite utilizar 64K para el c6digo y otros 64K para los datos. CUaJ}-
do se ejecuta un programa utilizando este modele, nunca cambian los re-
gistrosCS y DS. Todos los punteros utilizados para referenciar tanto el c6-
digoc6mo los datos son de 16bits, porque permanecemos dentro del limite
de los 64K.
Un puntero de 16bits que referencia un objeto dentro de un segmento
de 64K se denomina puntero near.
Sinuestro programa necesita mas de 64K para el c6digo 0 para los datos,
necesitaremosutilizar punteros de32 bits, en lugar de 16bits. Estos punte-
rospermiten apuntar a cualquier posici6n de la memoria; por ello, reciben
el nombre de punteros far. Las operaciones con estos punteros, (asigna-
cion,modificaci6n, etc.) requieren mas tiempo que las operaciones con los
punteros near.
La siguientefunci6n presenta en pantalla una ventana rellenada con un ca-
nictercar al cual Ie asociamos un atributo de subrayado, alta intensidad,
parpadeo, video inverso, normal 0 una combinaci6n de ellos.
Bajo MS-DOS, la memoria intermedia de pantalla con adaptador mo-
nocromoes de 4000 bytes, localizados a partir de la direcci6n 0 del seg-
mentoOxBOOO.La memoria intermedia de pantalla con adaptador color
gnificoses de 4000 bytes de longitud en modo texto y de 16384 bytes de
longitud en modo grafico, localizados, en ambos casos, a partir de la di·
reccion 0 del segmento OxB800.
EI atributo de representacion en pantalla se localiza en la parte alta
de la palabra; y el canicter a representar, en la parte baja.
const int PFILA = 11;
const int UFILA = 20;
const int PCOLN = 21;
const int UCOLN = 60;
II primera fila de la ventana
II ultima fila
II prim era columna
II ultima columna
struct atributos
{
unsigned int primer -plano : 3;
unsigned int intensidad : 1;
unsigned int color-fondo : 3;
unsigned int parpadeo : 1;
};
atributos atributo;
char car;
II bits 0 a 2
II bit 3
II bits 4 a 6
II bits 7
1********************************************************"*****
Rellenar una ventana en la pantalla con el cardcter car
void escribir(char car, char atributo)
{
int _far *p;
int fila, col;
p = (int ~ar *)OxB8000000; II asignar a p B800:0000
for (fila = PFILA; fila < = UFILA; fila + +)
for (col = PCOLN; col < = UCOLN; col + +)
*(p + fila * COLS + col) = car I atributa < < 8;
En esta funci6n, atributo contiene los atributos de los caracteres a re-
presentar en la ventana. Estos atributos tienen que localizarse en la parte
alta de la palabra de memoria correspondiente al canicter a representar.
De ahi, el ejecutar la sentencia:
la cual almacena en la palabra de direcci6np+ jila*COLS+col, el canicter
en la parte baja y los atributos en la parte alta. La variable p contiene la
direcci6n de comienzo de la pantalla, la direcci6n correspondiente a la fila
1 columna 1.
Para acceder a una direcci6n fuera del segmento de datos en el cual
estamos trabajando, utilizamos direcciones far. Como ejemplo observar la
sentencia:
Observar la cabecera de funci6n:
void escribir(char car, char atributo)
la cual indica, que la funci6n escribir debe recibir dos valores de tipo char:
cary atributo. EI que el argumento atributa en esta funci6n sea de un tipo
entero, es porque necesitamos realizar sobre eI un desplazamiento. Si he-
mos definido atributa como una estructura de campos de bits, segun se
observa en el ejemplo, la Hamada a la funci6n escribir tend ria que ser de
la forma:
escribir(car,(char)atributo);
la cual daria lugar a un error, ya que atributo es una estructura.
Para salvar este inconveniente recordar que un objeto de un determi-
nado tipo puede ser convertido a otro tipo cualquiera, utilizando conver-
siones explicitas de tipo sobre punteros como se indica a continuaci6n (ver
Conversion explicita del tipo de una expresion en el capitulo 2).
char ~trib = (char *)&atributo;
Los punteros huge se utilizan para referenciar datos; no se pueden utilizar
para referenciar codigo.
Para un puntero far Microsoft C asume que un objeto, codigo a da-
tos, esta dentro de un segmento de 64K; por ello las operaciones aritmeti·
cas sobre punteros far se realizan sobre los 16bits que indican el desplaza-
miento. Esta limitacion es superada utilizando los punteros huge; [as
operaciones aritmeticas sobre este tipo de punteros se realizan con los32
bits correspondientes a la direccion de un elemento de datos. Esto permite
que un unico elemento de datos supere el limite de los 64K.
int juge *hp;
int _far *hp;
hp+ +;
jp++;
Utilizando punteros near, far y huge, son el compilador y el enlazador
de C quienes se encargan del manejo de la memoria. Utilizando punteros
basados en un segmento, somos nosotros quienes tenemos que especificar
la direccion base del segmento. Las operaciones aritmeticas con punteros
EI puntero hp es incrementado utilizando el valor de 32 bits quere-
presenta la direccion segmentada (segmento mas desplazamiento). EIpun-
tero jp es incrementado utilizando solo el valor de 16bits que indica eldes·
plazamiento. Como una operacion aritmetica sobre 32 bits emplea mas
tiempo que una sobre 16 bits el trabajo con punteros huge es mas leota
que el trabajo con punteros far.
Un puntero basado en un segmento (based) 9cupa 16bits y tiene la paten-
cia y flexibilidad de un puntero far.
basados en un segmento (based pointer) se realizan sobre los 16 bits que
indican el desplazamiento.
Si optamos por un tamano para todos los punteros no es necesario decla-
rar cada variable como near 0 far. Lo mas simple es seleccionar un modelo
de memoria estandar para que sea el compilador quien se encargue de este
trabajo.
El programa de instalaci6n SETUP de C, instala las librerias para cada
uno de los modelos de memoria seleccionados. La utilizaci6n de estos mo-
delos evitan que nosotros tengamos que programar utilizando las palabras
clave _near y -Jar; es la forma mas simple de controlar el acceso al c6di-
go y a los datos en un programa; yes la mejor forma de escribir un progra-
ma portable. La desventaja es que con ellos, no siempre se obtiene el c6di-
go mas eficiente.
Memoria maxima
Modelo Codigo Datos Arrays
Pequenito (Tiny) <64K <64K <64K
Pequeno (Small) 64K 64K 64K
Medio (Medium) sin limite 64K 64K
Compacto (Compact) 64K sin limite 64K
Grande (Large) sin limite sin limite 64K
Enorme (Huge) sin limite sin limite sin limite
Cada modelo de memoria tiene su propia libreria, excepto el modele
enorme (huge) que utiliza la Iibreria del modelo grande (large) y el modele
pequenito (tiny) que utiliza la Iibreria del modelo pequeno (small). Lauti·
Iizacion de una u otra Iibreria depende de la que elijamos nosotros enel
momenta de compilar el programa.
Cuando escribimos un program a C, hay que tener presentes dos limi·
taciones que se aplican a los seis modelos de memoria:
• Un unico modulo no puede generar mas de 64K de c6digo. Signifi·
ca que un programa grande tiene que ser subdividido en modulos
mas pequenos, que compilaremos separadamente y que enlazare·
mos para formar un unico modulo ejecutable.
• Un unico elemento de datos no puede exceder de 64K, a no serque
aparezca en un programa compilado bajo e1mode1o de memoria
huge, 0 que dicho elemento se haya declarado con la palabra clave
Juge.
EI modelo pequenito Iimita un programa a un tamano maximo de 64K,
para codigo y datos combinados. La opcion IAT indica al compilador la
utilizacion de este modelo.
• Produce un fichero .COM que supera en ve10cidad de ejecuci6na
su equivalente fichero .EXE. EI fichero .COM, es en realidad un
programa generado con e1mode1o small y enlazado con la Iibreria
crtcom.lib con e1fin de Iimitar a un unico modulo de 64K el c6digo
y los datos. Por ejemplo:
cI IAT Ie prog.c
link Inoi Inoe Itiny crtcom.lib prog.obj,prog.com;
Heap
STACK
-.BSS
CONST
~ATA
;
NULL
_TEXT
_psp
En el modelo pequeiiito tanto e1c6digo como 10s datos, son accedi-
dos con direcciones near.
EImodelo pequeno utiIiza Ios dos segmentos por defecto, uno para c6digo
y otTOpara datos. La opci6n /AS indica aI compilador Ia utiIizaci6n de
estemodelo. Es la opci6n por defecto.
Heap
STACK
_BSS
CONST
_DATA
NULL
_TEXT
_psp
t 64K Maximo
t 256 bytes
_ DS:OOOO,SS:OOOO
_ CS:OOOO
Por defecto, en el modelo pequeno, tanto al c6digo como a los datos
se accede con direcciones near.
El modelo medio utiliza varios segmentos para c6digo y un unico segmen·
to para datos. La opci6n lAM indica al compilador la utilizaci6n de este
modelo.
Heap
STACK
_BSS
CONST
~ATA
NULL
_TEXTJ 64K Maximo
64K Maximo m6dulo _TEXT
--+------ t------.., - CS cargado
_ DS:OOOO,SS:OOO
_ CS:oooo
Por defecto, en el modelo medio, al c6digo se accede con direcciones
far y a los datos se accede con direcciones near.
EI modelo compacta utiliza un segmento para c6digo y varios segmentos
para datos. La opci6n lAC indica al compilador la utilizaci6n de este modelo.
Far Heap
Near Heap
STACK
_BSS
CONST
~ATA
NULL
64K Mciximo m6dulo~ATA _ DS cargado
Por defecto,en el modelo compacto, at c6digo se accede con direccio-
·nes near y a los datos se accede con direcciones far.
EI modelo grande utiliza varios segmentos para c6digo y varios segmentos
para datos. La opci6n IAL indica at compilador la utilizaci6n de este modelo.
Por defecto, en el modelo grande, tanto al c6digo como a los datos
se accede con direcciones far.
Far Heap
Near Heap
STACK
--.BSS
CONST
-DATA
NULL
64K Maximo m6dulo .-DATA DS d
---+.--- ~-----_l - carga 0
64K Maximo _TEXT......L ~-----_l _ CS:OOOO
64K Maximo mdoulo_ TEXT _ CS cargado
256 bytes _psp
El significado de cada uno de los bloques de las figuras anteriores es
el siguiente:
El stack tiene un tamano de 2K por defecto y puede
llegar a 64K. Este tamano es fijado en tiempo de com-
pilacion. Utilizar la opcion "IF nUITL-hex" para asig-
nar un valor, especificado en hexadecimal, diferente
de 2K. El stack es utilizado para almacenar tempo-
ralmente: datos locales (auto),los argumentos actua-
les y la direccion de retorno cuando se llama a una
funcion, y valores temporales generados al evaluar una
expresion.
Este segmento es inicializado a cero cada vez que se
ejecuta un programa. Contiene datos estaticos y glo-
bales no inicializados (excepto -far, _based, 0
_huge). Esto explica por que todas las variables ex-
terny staticson automaticamente inicializadas a cera.
Contiene datos const, cadenas de caracteres, y cons-
tantes en coma flotante.
Segmento de datos por defecto. Datos estaticos y glo-
bales inicializados (excepto -far, _based, 0 _huge).
Microsoft C define un segmento NULL, aparentemen-
te de 8 bytes de longitud, (tamano del tipo mas gran-
de; double)para chequear asignaciones a punteros nu-
los, normalmente producidas por no haber asignado
previamente memoria dinamica para el dato. Este seg-
mento es inicializado a cero cada vez que se ejecuta
un programa. Si hemos utilizado la opcion /Zr del
compilador una asignacion en una posicion de esta
memoria es inmediatamente detectada y el programa
finaliza. Si no hemos utilizado la opcion IZr tales asig-
naciones son detectadas y comentadas al finalizar el
programa.
Segmento de codigo por defecto para todos los mo-
delos de memoria (excepto -far, _based, 0 _huge).
segmento adicional para datos estaticos y globales ini-
cializados y no inicializados -far, _based, 0 _huge.
segmento adicional para codigo -far para los mo-
delos medio, grande y enorme.
(program segment prefix). ,Segmento de 256 bytes afia-
dido al comienzo del programa. Contiene la direcci6n
de la tabla de las variables de entorno para el progra-
ma y una copia de la linea de 6rdenes de DOS nece-
saria para los argumentos argc y argv de la funci6n
main( ).
El modelo enorme utiliza varios segmentos para c6digo y varios segmen-
tos para datos. La opci6n IAH indica al compilador la utilizaci6n de este
modelo.
El modelo huge es parecido al modelo large, con la excepci6n de que
soporta arrays mayores de 64K, con las siguientes limitaciones:
• Un array auto no puede ser declarado _huge. Un array puede ser
declarado _huge si es static, 0 si la memoria ocupada por el, es
asignada por la funci6n halloc( ).
• Para cualquier array mayor de 128K, todos los elementos deben te-
ner un tamafio en bytes igual a una potencia de 2. Si el tamafio del
array esta entre 64K y 128K, ambos inclusive, sus elementos pueden
ser de cualquier tamafio.
El tamafio en bytes de un array huge, es un valor de tipo unsigned long.
Por ello, para calcular el tamafio de un array huge utilizando la funci6n
sizeof( ) que devuelve un valor de tipo unsigned int hay que utilizar la no-
taci6n cast.
De forma similar debemos operar cuando realicemos una diferencia
de punteros huge.
/. Compi/ar bajo el modelo huge ./
# include <stdio.h>
int a[40001J;/. tamano = 80002 bytes ./
main( )
(
a[OJ = 100;
a[40000J = 200;
printf(H%d %d n': a[OJ,a[40000J);
I
Por convenio un puntero nulo representa una direcci6n de memoria reser-
vada para utilizarla como el valor centinela de una direcci6n de memoria
invalida. Ellenguaje C define un puntero nulo por medio de la macro NULL:
int i;
char car = 's: *p = NULL;
printj(Hdireccion de car: %lp  n': (char -far *)&car);
printj(Hdireccion de p: %lp  n': (char -far *)&p);
printj(Hp apunta a: %lp  n  n': (char -far *)p);
/ * Contenido del segmento NULL.
* Direccion de comienzo = DS:OOOO
*/
EI programa que se expone a continuaci6n muestra como se detecta
unaasignaci6n realizada sabre una direcci6n no valida.
/. Compi/ar bajo el modelo small ./
# include <stdio.h >
void main(void)
{
for (i = 0; i < 8; i+ +)
printjr<%02X': *(p+i));
1* Asignar un valor *1
*P = <n:·
printj(H  n  nlo apuntado es:
1* Contenido del segmento NULL.
* Direccion de comienzo = DS:OOOO
*1
for (i = 0; i < 8; i + +)
printj(H%02X ': *fP+i));
putchar«  n');
I
Si el programa se compila sin la opci6n IZr su ejecuci6n produce la
salida:
direcci6n de car:
direcci6n de p:
p apunta a:
22ED:ODCA
22ED:ODCC
22ED:OOOO
run-time error R6001
- null pointer assignment
Si el programa se compila con la opci6n IZr (cl /qc IZr prog.c) su eje-
cuci6n produce la salida:
direcci6n de car:
direcci6n de p:
p apunta a:
22ED:ODCA
22ED:ODCC
22ED:OOOO
run-time error R6012
- ilegal near pointer use
En el primer caso, Microsoft C chequea el segmento NULL antes de
que el programa finalice para detectar si se ha escrito algo. Podemos ob·
servar que se ha escrito el canicter "n" (6E). El programa finaliza y se yj.
sualiza un mensaje de error sin ninguna indicaci6n de d6nde se ha produ-
cido. La escritura sobre el segmento NULL se produce independientemente
de que peste 0 no inicializado al valor NULL.
En el segundo caso, en cuanto se produce la primera asignaci6n a una
direcci6n no valida el programa termina y se visualiza el mensaje corres-
pondiente. Esto quiere decir que el segmento NULL es chequeado cada
vez que se hace una asignaci6n sobre una posici6n de memoria referencia-
da por un determinado puntero.
En un modele estandar todos los punteros referentes a datos son del mis-
mo tamafio y todos los punteros referentes a c6digo tambien son del mis-
mo tamafio. En cambio un modele de memoria mixto combina punteros
de diferentes tamafios dentro del mismo programa.
/ * Compi/ar bajo el modelo small */
# include <stdio.h>
int a[30000}, b[30000};
main( )
[
a[O} = 100;
b[O} = 200;
printft'%d %d  n': a[O}, b[O]);
1
Al compilar este program a bajo el modelo small obtenemos un error
debido a que los datos sobrepasan ellfmite de los 64K. Una primera solu-
ci6n a este problema es elegir el modelo compact. Sin duda esta es la mejor
elecci6n en cuanto a transportabilidad del programa. Una segunda solu-
ci6n, y quizas mejor en cuanto a velocidad de ejecuci6n, es utilizar el mo-
delo small que usa punteros near y declarar algunos elementos de datos
como far.
* Campi/ar bajo el modelo small */
# include <stdio.h >
int aI30000], _far bI30000];
main( )
I
alO] = 100;
blO] = 200;
printj(<t%d %d  n': aIO],brO]);
J
Microsoft C permite utilizar punteros de diferentes tamafios, para 10
queafiade las palabras clave .-near, -far, _huge, y _based. Estas pala-
bras no pertenecen allenguaje C estandar, sino que son una extensi6n de
Microsoft C. Su significado cuando se utilizan con datos 0 con c6digo es
el siguiente:
Losdatos son direccionados con 16bits y se almacenan en el segmento de
datos por defecto. Las funciones son direccionadas con 16 bits y se alma-
cenan en el segmento de c6digo actual. Las operaciones aritmeticas con
punteros se realizan con 16 bits.
Losdatos son direccionados con 32 bits y pueden 10calizarse en cualquier
parte de la memoria. Las funciones son direccionadas con 32 bits, por 10
quepueden ser llamadas desde cualquier posici6n de memoria. Las opera-
ciones aritmeticas con punteros se realizan con 32 bits.
Los datos son direccionados con 32 bits y pueden localizarse en cualquier
parte de la memoria. Elementos individuales de datos (arrays) pueden 50-
brepasar ellimite de los 64K. Estos punteros no se pueden aplicar sabre
el codigo. Las operaciones aritmeticas con punteros se realizan con 32 bits.
Los datos pueden localizarse en cualquier parte de la memoria y son direc-
cionados con 16 bits mas una direccion base conocida, 10 que da lugar a
un direccionamiento con 32 bits. Estos punteros no se pueden aplicar 50-
bre el codigo. Las operaciones aritmeticas con punteros se realizan can 16
bits. '
Cuando declaramos punteros utilizando las palabras clave--'lear, -far,
~uge, 0 _based, en algunos casos puede ocurrir que sean incompatibles
con las funciones de la libreria estandar. En estos casos, utilizando la no-
tacion cast, un puntero near puede convertirse a un puntero far, porque
el compilador afiade la direccion base del segmento correspondiente; pero
si convertimos un puntero far a un puntero near, el compilador generani
un mensaje para avisarnos de que el desplazamiento obtenido puede no
corresponder al segmento de datos por defecto.
Utilizando el modele small siempre podremos pasar a una funci6n el
valor de un elemento declarado far pero no su direccion.
/ * Compi/ar en el modelo small */
# include <stdio.h >
# include <time.h >
long -for seg;
main( )
[
time(&seg); / * direcci6n for i/egal */
printj("%ld n': seg); /* valor legal */
}
La declaracion de un objeto 0 de un puntero a un objeto puede modificar-
se utilizando las palabras clave: -'lear, -far, --'zuge, 0 _based. Cuando
seutilizan estas palabras clave hay que tener en cuenta las siguientes reglas:
• La palabra clave siempre modi fica al objeto 0 al puntero situado
inmediatamente a su derecha. Por ejemplo:
• Si a la derecha de la palabra clave declaramos un objeto, la palabra
clave determina donde se ubicanl el objeto: en el segmento de datos
por defecto (-'lear) 0 en un segmento de datos separado (-far,
--'zuge, 0 _based). Por ejemplo:
p es un puntero far a un objeto de tipo char, que sera almacenado
en el segmento de datos por defecto independientemente del mode-
10 de memoria utilizado.
• Si a la derecha de la palabra clave declaramos un puntero a un obje-
to, la palabra clave determina el tipo de direccion que contendra el
puntero: una direccion near (16 bits), una direccion far (32 bits),o
una direccion huge (32 bits).
En la declaraci6n de una funci6n s610 se pueden utilizar las palabras clave
_near 0 -Jar. Cuando se utilizan hay que tener en cuenta las siguientes
reglas:
• La palabra clave siempre modifica la funci6n 0 el puntero a la fun-
ci6n situado inmediatamente a su derecha.
• Si a la derecha de la palabra clave declaramos una funci6n, la pala-
bra clave determina c6mo se asignani la funci6n: near 0 far. Por
ejemplo:
jx( ) es una funci6n direccionada con 32 bits que retorna un valor
de tipo char.
• Si a la derecha de la palabra clave declaramos un puntero a una fun-
ci6n, la palabra clave determina c6mo sera Hamada la funci6n: uti-
lizando 16 bits (_near) 0 32 bits (-Jar). Por ejemplo:
• La declaraci6n de una funci6n debe coincidir con la cabecera de la
definici6n de dicha funci6n. Por ejemplo:
char _far jx(void)
[
EI concepto de punteros basados en un segmento (_based) fue introduci-
do por Microsoft en el compilador C, versi6n 6.00. Este tipo de punteros
esta restringido a tipos de datos, no pudiendose utilizar como punteros a
funciones. Ocupan dos bytes y tienen la potencia y flexibilidad de un pun-
tero far.
Para declarar un puntero based Microsoft ha afiadido las siguientes
palabras clave:
Tipo puntero. Igual que _near, -Jar, 0
_huge. La palabra clave _based siempre
va seguida de una expresion entre parente-
sis que indica la direccion base.
Tipo segmento. Permite declarar variables
para contener direcciones de segmentos.
Especifica una direccion base que es la co-
rrespondiente a "segmento".
Especifica una direccion base que es la co-
rrespondiente al segmento al cual pertene-
ce el puntero.
_NULLSEG Segmento nulo. La definicion para esta con stante esta en
malloc.h y es de la forma: ((_segment)O).
_NULLOFF Desplazamiento nulo. La definicion para esta con stante esta
en malloc.h y es de la forma:
Operador base. Combina un segmento y un desplazamien-
to, dando como resultado la direcci6n efectiva:
---Segment seg = OxlA47;
iot _based(void) *l1esp (void *)Ox1234;
inti a = *(seg:>desp);
lA470
+ 1234
/ ***** Variables y punteros basados en un segmento constante *****
/ * Compilado bajo el modelo small */
# include <stdio.h >
int main(void)
(
int _based(~egname(H_CODE")) *pl;
int _based(~egname(H---.DATA")) *p2;
iot _based(~egname(H_CONST")) *p3;
iot _based(~egname(H_STACK")) *p4;
iot _based(~egname(HMISEG")) *p5;
int a=O, b[500];
b[lO]= 25;
printj(H  nCode
printj(H  nData
%lp':
%lp':
(iot _far *)pl);
(int _far *)p2);
printf(H  nConst
printf(H  nStack
printf(H  nMiseg
I
%lp': (int _far *)p3);
%lp': (int _far *)p4);
%lp  n': (int _far *)p5);
Code
Data
Const
Stack
Miseg
21D6:0000
22FO:OOR8
22FO:D88B
22FO:680E
22EF:A326
Observar que el resultado obtenido coincide con el mapa de memoria
correspondiente al modelo small. Los segmentos DATA, CONST y STACK
tienen su origen en el segmento de datos (DS). Observar tambien que se
ha introducido un nuevo segmento, MlSEG, entre los segmentos de codigo
y de datos; de forma similar a como se ubican 105 segmentos de datos en
el modelo compact.
La caracteristica clave de las variables de tipo ~egment es que pueden
utilizarse para declarar otras variables _based.
1***********Punteros basados en un segmento variable ***********/
1* Compi/ado bajo el modelo small */
# include <stdio.h>
# include <string.h>
# include <malloc.h >
~egment segmentol, segmento2;
char _based(segmentol) *p, _based(segmento2) *q;
void main(void)
{
if ((segmento1 = _bheapseg(2048)) = = .-NULLSEG)
(
puts("error 1: segmento no asignado");
exit(l);
J
if ((segmento2 = _bheapseg(1024)) = = .-NULLSEG)
(
puts("error 2: segmento no asignado");
exit(2);
J
if ((p = _bmalloc(segmento1, 81)) = = .-NULLOFF)
(
puts("error 3: insujiciente espacio de memoria ");
exit(3);
J
if ((q = _bmalloc(segmento2, 81)) = = .-NULLOFF)
(
puts("error 4: insujiciente espacio de memoria");
exit(4);
J
printj("segmento 1 = %p  n': segmento1);
printj('p = %lp  n': (char _far *)p);
-fstrcpy((char _far *)p,
(char _far *) "texto rejerenciado por el puntero p");
printj("%Fs  n': (char _far *)p);
printj(" nsegmento 2 = %p  n': segmento2);
printf("q = %lp  n': (char _far *)q);
-fstrcpy((char _far *)q,
(char _far *) "texto rejerenciado por el puntero q ");
printj("%Fs  n': (char _far *)q);
/ * Liberar memoria */
_bjreeseg(segmento1);
_bjree(segmento1, p);
_bjreeseg(segmento2);
_bjree(segmento2, q);
J
segmento 1 = 3330
p = 3330:0016
texto referenciado por el puntero p
segmento 2 = 33B3
p = 33B3:0016
texto referenciado por el puntero q
La funcion _bmalloc asigna, dentro del segmento especificado, un
bloque de memoria igual al numero de bytes indicados. Si no hay suficien-
te espacio de memoria, la funcion devuelve el valor ~ULLOFF. La fun-
cion _bfree( ) libera el bloque de memoria asignado por la funcion
_bmalloc.
Observar que para copiar una cadena.de caracteres en otra hemos uti-
lizado la funcion -!strcpy( ), la cual acepta como argumentos punteros
far. Aunque la compilacion se ha hecho bajo el modelo small, no podemos
utilizar la funcion strcpy( ) porque esta espera como argumentos punteros
near.
La funcion _bheapseg( ) da como resultado la direccion base de un seg-
mento creado en el area de memoria libre.
Si el segmento no se puede crear, esta funcion devuelve el valor
-.NULLSEG (segmento nulo).
Esta funci6n libera el segmento de memoria creado par la funcian
_bheapseg( ).
Esta funci6n retorna un valor 0 si se ejecuta satisfactoriamente 0 un
valor -1 si ocurre un error.
Un puntero based puede utilizar como direcci6n base otro puntero. Este
tipo se aplica s610 a variables que sean punteros.
El siguiente programa utiliza un puntero pb, basado en el puntero base,
para almacenar valores float en el buffer mas adecuado entre varios de-
finidos.
/ *************** Puntero basado en otro puntero ***************1
/ * Compi/ado bajo el modelo small */
/ * Declaracion de buffers */
char bufferJ[200];
char buffer2[400];
char buffer3[800];
char buffer4[J600];
char *base;
float _based(base) *pb;
/ * Almacenar las direcciones de los buffers */
char ~irbuf[ ] = {bufferJ, buffer2, buffed, buffer4};
1*Almacenar los tamanos de los buffers */
iot tambuf[ ] = (sizeof(buffer1)/sizeof(f1oat),
sizeof(buffer2)/sizeof(f1oat),
sizeof(bufferJ) /sizeof(f1oat),
sizeof(buffer4)/sizeof(f1oat) I;
1* numbuf = nOde buffers */
iot numbuf = sizeof(tambuf)/sizeof(int);
void main(void)
(
iot n_elem, i = 0;
system(Hcls");
printjt';.Cudntos valores se van a almacenar? ");
scanj(H%d': &n_elem);
1* Buscar un buffer de tamano adecuado */
while (i < numbuf)
if (tambuf[iJ < n_elem)
i++;
else
break;
if (i = = numbuf)
(
puts(Hbuffer no disponible");
exit(l);
I
base = dirbuf[iJ;
printj(Hutilizando el buffer %d  n': i);
printj(Hdisponibles %d elementos  n': tambuf[i]);
printj(Halmacenando los %d valores:  n  n': n_elem);
for (i = 0; i < n_elem; i+ +)
{
pb[i] = 3.1416 * i * i;
printjt<%10.2f r': pb[i]);
Este ejemplo crea cuatro buffers y elige el mas adecuado para almace-
nar un conjunto de valores.
La declaraci6n de un puntero _based(void) crea un puntero generico que
actua igual que un desplazamiento dentro de un segmento. Esto quierede-
cir que podremos definir cualquier segmento y combinarlo con un puntero
declarado _based(void) utilizando el operador ": >". Este tipo se apliea
s610 a variables de tipo puntero.
El siguiente ejemplo utiliza la variable segmento de tipo ----.Segment
para definir el comienzo del segmento; y el'puntero pb como desplazamiento
dentro del segmento.
/ ******************* Puntero basado en void *******************/
/ * Compilado bajo el modelo small */
-segment segmento = OxB800; / * Sustituir por OxBOOOpara mono */
int _based(void) *pb = 0;
void main(void)
{
int fila, col;
FILE *pf;
/ * Volcado de la pantalla al fichero "pan" */
if ((pf = fopen("pan':'w")) = = NULL)
(
puts("error: no se puede abrir el fichero");
exit(l);
J
for (fila = 0;fila < 25; fila + +)
{
for (coI = 0; col < 80; col+ +, pb+ +)
fputc((char)*(segmento: >pb), pf);
fputc('  n: pf);
Este ejemplo realiza un volcado de una Pe;mtalla de texto sobre un fi-
chero denominado "pan". Cada posici6n de la pantalla en modo texto est a
representada por dos bytes. El byte de menor peso contiene el caracter y
el byte de mayor peso sus atributos. En el fichero s610 salvamos el caracter.
Los punteros basados en su propio segmento (self) son titHes cuando de-
claramos objetos based que contienen miembros que son punteros. Este
tipo se aplica s610 a variables de tipo puntero. Por ejemplo: la definici6n
de un nodo de una estructura en arbol.
1************Puntero basado en su propio segmento ***********d
1* Compi/ado bajo el modelo small */
# include <stdio.h>
# include <malloc.h >
# include <string.h>
struct nodo_arbol
[
char clave[20};
nodo _based((_segment).-Self) *izdo;
nodo _based((_segment).-Self) *dcho;
};
void main(void)
[
nodo _based(_segname("MISEG")) *raiz;
nodo _based(_segname("MISEG")) *p;
raiz = NULL;
p = _bmalloc(_segname("MISEG"), sizeof(nodo));
-fstrcpy((char _far *)p->clave, (char _far *) "12ABOO");
p- >izdo = p- >dcho = NULL;
raiz = p;
printj("Data seg. = %p  n': _segname("~ATA"));
printf(S'Miseg = %p  n'; -----..Segname(HMISEG"));
printfrscJave = %lp'; (~har .-far .)&raiz- >clave);
printf,., %Fs  n'; (char ~ar .)raiz- >clave);
printf(Hizdo = %lp  n': (nodo ~ar .)&raiz- >izdo);
printf(Hdcho = %lp  n'; (nodo .-far .)&raiz- >dcho);
I
Data seg.
Miseg
clave
izdo
dcho
= 22F2
= 22Fl
22Fl:FFFF
22Fl:0013
22Fl:0015
Cuando trabajamos con punteros near, far, 0 based, necesitamos fundo-
nes compatibles con estos, que nos permitan asignar, reasignar, y Iiberar
memoria. Estas funciones estan declaradas en el fichero malloc.b, y son
las siguientes:
---Jlmalloc
_fmalloc
_bmalloc
---Jlcalloc
_fcalloc
_bcalloc
_nrealloc
~realloc
_brealloc
---Jlfree
Jfree
_bfree
que vimos en el capitulo de "punteros" con la salvedad del atea de memo-
ria sobre la que trabajan: memoria near, memoria far, 0 memoria based,
10 cual es trans parente al usuario. Los formatos correspondientes son los
siguientes:
void _based(void) * _bFealloc(segment seg,
void _based(void) *P. size_t t);
Esta funcion cambia el tamafto de un bloque de memoria previamente asig-
nado, sin modificar su posicion en memoria. Es igual que la funcion
real/oc( ), excepto que esta ultima puede modificar la posicion de memo-
ria del bloque.
Si hay insuficiente espacio de memoria la funcion retorna un puntero
nulo (NULL).
En un ejemplo anterior, no hemos podido utilizar la funci6n strcpy() por-
que esta espera como argumentos punteros near; en su lugar hemos utili·
zado la funci6n -!strcpy( ), la cual acepta como argumentos punteros far.
Esta es la raz6n de la existencia de las funciones que se enumeran a conti-
nuaci6n:
_fstrcat
_fstrcspn
_fstrncpy
_fstrtok
_fstrnset
_fstrchr
_fstrlen
_fstrrchr
_fstrlwr
_fstrdup
_fstrcmp
_fstrncat
_fstrspn
_fstrupr
_fstrcpy
_fstrncmp
_fstrstr
_fstrset
Estas funciones, salvando la idea de que han side disefiadas para aceptar
argumentos far, tienen el mismo significado que las descritas en el "capi-
tulo 5" sin el prefijo _f. Como ejemplo, la funci6n para comparar dos
cadenas de caracteres tiene la forma:
Las declaraciones para estas funciones se encuentran en el fichero
string.h.
Las funciones que se describen a continuaci6n trabajan con areas deme·
moria (buffers); entendiendo por area de memoria el espacio ocupado por
una estructura cualquiera (struct, uni6n, array etc.).
Copia cero 0 mas bytes desde un buffer fte a otro des. EI ultimo byteco-
piado es el primer byte de fte que coincida con el byte especificado por
c, 0 cuando se hayan copiado n bytes; la condici6n que se de primero.
# include <string.h > 0 <memory.h >
void *memccpy(void *-lies, void 4te, int c, unsigned int n);
Esta funcion retorna un puntero al siguiente byte en des si c es copia-
do. En otro caso, retorna un puntero nulo (NULL).
Busca el byte c, en el buffer buf. La busqueda finaliza cuando el byte c
es encontrado 0 cuando se hayan explorado Ios n primeros bytes.
Esta fundon retorna un puntero al primer byte que se encuentre en
buf En otro caso, si no aparece c retorna un puntero nulo (NULL).
Esta funcion compara Ios primeros n bytes de bun y buf2, distinguiendo
mayusculas y minusculas, y devuelve un valor:
<0 si bun es menor que buf2,
= 0 si bun es igual a buf2 y
>0 si bun es mayor que buf2
Esta fundon tiene el mismo formato y realiza Ia misma fundon que
memcmp(), excepto que no hace distincion entre mayusculas y minusculas.
Copia n bytes desde el buffer fte al buffer des. Si los buffers fte y desestan
solapados, el resultado es impredecible.
Copia n bytes desde el buffer fte al buffer des. Si los buffers fte y desesi~
solapados, el resultado es el esperado.
# include <stdio.h >
# include <string.h >
char fuente[20j = "abcdefghijKLMNO";
char destino[20j;
main( )
!
char *P;
char :kSolapado = &(fuente[10));
size_t contal = sizeof(fuente);
size_t conta2 = strlen(fuente);
/ * contal = 20 */
/ * conta2 = 15 */
printj("Buffer inicial para todas las operaciones:  n");
printj("%s  n  n': fuente);
printj("Despues de ejecutarse la funci6n indicada  n  n ");
/ * Buscar un cardcter */
p = memchr(fuente, 'K: conta2);
printj("memchr: buscar K  n");
printj("fuente = %s  nresultado = %s  n  n': fuente, p);
/ * Copiar. No hay solapamiento entre buffers */
memcpy(destino, fuente, conta2);
printj("memcpy: sin solapamiento  n");
printj("fuente = %s  ndestino = %s  n  n': fuente, destino);
/ * Copiar. Hay solapamiento entre buffers */
memcpy(solapado, fuente, conta2);
solapado[conta2j = ' 0:-
printj("memcpy: con solapamiento  n ");
printj("fuente = %s  nsolapado = %s  n  n': fuente, solapado);
/ * Rellenar con un cardcter */
memset(destino, ' 0: contal),-
/ * Restaurar fuente */
memccpy(fuente, "abcdefghijKLMNO': ' 0: contal),-
/ * Copiar. No hay solapamiento entre buffers */
memmove(destino, fuente, conta2);
printf("memmove: sin solapamiento  n");
printj("fuente :::;:%s  ndestino :::;:%s  n  n': fuente, destino);
/ * Copiar. Hay solapamiento entre buffers */
memmove(solapado, fuente, conta2);
solapado[conta2] = ' 0';
printf(Hmemmove: con solapamiento  n");
printf(Hfuente = %s  nsolapado = %s  n  n': fuente, solapado);
}
Buffer inicial para todas las operaciones:
abcdefghijKLMNO
memchr: buscar K
fuente = abcdefghijKLMNO
resultado = KLMNO
memcpy: sin solapamiento
fuente = abcdefghijKLMNO
destino = abcdefghijKLMNO
memcpy: con solapamiento
fuente = abcdefghijabcdefghijabcde
solapado = abcdefghijabcde
memmove: sin solapamiento
fuente = abcdefghijKLMNO
destino = abcdefghijKLMNO
memmove: con solapamiento
fuente = abcdefghijabcdefghijKLMNO
solapado = abcdefghijKLMNO
SOPORTE MS-DOS PARA MANIPULACION DE BLOQUES DE
MEMORIA
Cuando se trabaja bajo MS-DOS con argumentos far, utilizaremos lassi·
guientes versiones de las funciones anteriores:
void _far * _far _fmemccpy(void _far *lies, void _far *fte,
int c, unsigned int n);
int _far _fmemcmp(const void _far *bufl, const void _far *buf2,
size_t n);
int _far _fmemicmp(const void _far *bufl, const void _far *buf2,
size_t n);
void _far * _far _fmemcpy(void _far *lies, const void _far *fte,
size_t n);
void _far * _far _fmemmove(void _far *lies, const void _far *fte,
size_t n);
Entorno de programaci6n basado en ventanas que incor-
pora un editor de textos, un compilador, un enlazador, un
depurador, la utilidad Make, un analizador de c6digo fuen-
te, y un sistema de ayuda en linea.
El paquete Microsoft C incluye varios programas de uso general. Entre ellos
estan:
Es el programa que controla la compilaci6n. Permite com-
pilar y enlazar programas fuente. Microsoft C incluye dos
compiladores C separados: el compilador total y el com-
pilador rapido.
El compilador utilizado por defecto es el compilador to-
tal. Para utilizar el compilador rapido hay que especificar
la opci6n /qc.
El compilador rapido no puede ejecutar much as de las op-
timizaciones que permite el compilador total, pero a dife-
rencia de este, permite la compilaci6n incremental (opci6n
IGi). La compilaci6n incremental aumenta la velocidad de
compilaci6n ya que el compilador solamente compila aque-
llas funciones que, des de la ultima compilaci6n, han sido
modificadas.
LINK.EXE Programa utilizado para en lazar ficheros objeto produci-
dos por CL Ie con las librerias apropiadas para producir
un fichero ejecutable.
ILINK.EXE Es el enlazador incremental. El enlace ofrece las mismas
ventajas que la opci6n IGi del compilador. IUNK sola-
mente enlaza aquellos m6dulos que han cambiado desde
el ultimo enlace.
Para crear de un programa fuente C un programa ejecutable bajo DOS,
los pasos a seguir son:
2. Compilar cad a fichero fuente creando el correspondiente fichero
objeto utilizando la orden CL.
3. Se ejecuta la orden LINK para en lazar los ficheros objetos, lasti·
brerias especificadas y las librerias del sistema. El resultado esun
fichero ejecutable (.exe)
Si el programa fuente C esui compuesto por un unico m6dulo, lospa
sos 2 y 3 pueden realizarse ambos con la orden CL.
Permite com pilar 0 compilar y enlazar uno 0 mas ficheros fuente C.
fichero de salida, si no se especifica 10 contrario, tendra el mismo nomb
que el primer fichero especificado.
CL [opeton]... jiehero [[opeton]... Uiehero]...]...
[/link [lib... ope-link]]
indica cualquier opci6n de CL. Se utilizan para compilar el
program a bajo un as condiciones especificas. Estas afectan a
cualquier fichero que aparezca posteriormente en la linea de
6rdenes. Puede verse un resumen de estas opciones ejecutan-
do CL /HELP. Las opciones hay que escribirlas tal cual se
indican, ya que se hace distinci6n entre mayusculas y mi-
nusculas.
nombre del fichero fuente a compilar, del fichero objeto a en-
lazar 0 de la Iibreria que se desea pasar al proceso de enlace.
indica el nombre de la libreria (.Lffi) que se desea pasar al pro-
ceso de enlace.
indica una 0 mas opciones para el proceso de enlace. Normal-
mente estas opciones no son necesarias excepto cuando se re-
quiere ejecutar operaciones especiales, como por ejemplo cam-
biar el tamafio del stack. Las opciones se escriben a
continuaci6n de /link y van precedidas por /. Ver en este mis-
mo capitulo la orden LINK.
El numero de opciones, nombres de ficheros y nombres de librerias
puede ser cualquiera con la unica limitaci6n de que la linea de 6rdenes no
puede exceder de 128 caracteres.
En este ejemplo la orden CL ejecuta las acciones de compilar y enla-
zar bajo las especificaciones del modele grande de memoria. Los ficheros
fuente .e, son compilados y los ficheros objeto .ob} y librerias .lib especifi-
cados son enlazados. Este proceso es descrito a continuaci6n e ilustrado
con la figura que se muestra:
1. CL compila Ios ficheros fuente especificados creando ficheros ob-
jeto (.OBJ).
2. CL llama al programa enlazador (link) y Ie pasa Ios ficheros obje-
to resultantes de Ia compilaci6n, mas Ios especificados, mas las
librerias.
3. LINK enlaza estos ficheros objeto, Ias librerias especificadas y las
librerias del sistema, y crea un fichero ejecutable (a.exe en el
ejemplo).
C
E
B-- 0
D M
I
P
I
T L
0 A
R D
0
R
c.obj
E
N
L
A
Z
A
• D
o
R
0--
En un nombre de fichero no se hace distinci6n entre maytisculas y mi·
miscula,:. Cualquier nombre de fichero puede especificarse mediante elca·
mino correspondiente.
Este ejemplo compila a.c, creando a.ob} y pasa a.ob}, b.ob} y graphics. lib,
al programa enlazador para crear el fichero ejecutable a.exe.
Cuando no se especifica el camino para un fichero, CL asume que este
se encuentra en el directorio actual de trabajo.
Sino se tiene la seguridad de que el programa coja en la memoria dis-
ponible,podemos crear overlays. Un overlay es una parte del programa (m6-
dulo)almacenada sobre el disco; la cual sera cargada en memoria cuando
serequiera su ejecuci6n. Para especificar que un determinado m6dulo se
va a tratar como un overlay, se incluye entre parentesis.
Este ejemplo crea un program a Hamado modJ.exe con un overlay de-
nominado mod2. Cuando durante la ejecuci6n, el control pase a mod2,
este sera cargado en memoria y ejecutado.
Un identificador de fichero bajo DOS consta de dos partes: un nombre
de hasta 8 caracteres y una extension de hasta 3 caracteres separada del
nombre par un pun to.
Significado
CL asume el fichero como un fichero fuente C y 10
compila.
CL asume el fichero como un fichero objeto y 10 pasa al
programa enlazador (linker).
CL asume el fichero como una libreria y 10 pasa al pro-
grama enlazador.
CL asume el fichero como un fichero objeto y 10 pasaal
programa enlazador. Cuando el nombre del fichero no ten·
ga extensi6n, este debe finalizarse con un punto, de 10 con-
trario, CL asumini la extensi6n .OBJ.
Las opciones comienzan con el canicter "I" 0 con el caracter "." y deben
utilizarse exactamente igual que se describen, esto es, con la misma combi·
naci6n de letras maytisculas y minusculas. A continuaci6n se describen las
opciones para la orden CL agrupadas segun la funci6n que desempefian.
instrucciones 8086. Opci6n por defecto
instrucciones 80186
instrucciones 80286
convenio -fortran 0 -pascal de Hamada a funciones
convenio estandar _cdecl de Hamada a funciones. Opci6n
por defecto.
permite el chequeo de la pila. Opci6n por defecto
compilaci6n incremental. Implica ILL Utilizar junto con
la opci6n Iqc.
almacena las cadenas en el segmento CONST. No dispo·
nible con la opci6n Iqc
/00
101
102
10c
10d
10e
10i
/Gs
/Gt[num]
convenio -fastcall de Hamada a funciones. Cuando es po-
sible, los valores son pasados a los registros de la UCP en
lugar de hacerlo sobre la pHa.
se suprime el chequeo de la pila
coloca elementos de datos mayores 0 iguales que num bytes
en un nuevo segmento. Por defecto num es 256. S610 se
puede utilizar con los modelos compacto, grande y enor-
me. Es util para programas que tienen mas de 6~K en pe-
quefios elementos de datos estaticos y glob ales inicia-
lizados.
c6digo de entrada/salida para utilizar en aplicaciones'bajo
Microsoft Windows.
igual que /Gw, pero genera c6digo de entrada mas eficiente.
selecciona la libreria matematica alternativa.
No disponible con la opci6n /qc
selecciona la libreria para emular el 80x87 si este no est a
presente. No disponible con la opci6n /qc
selecciona la libreria 80x87. Requiere un coprocesador
80x87. No disponible con la opci6n /qc
genera instrucciones en linea para un coprocesador 80x87
y selecciona la libreria para emular el 80x87 si este no esta
presente. Es la opci6n por defecto.
genera instrucciones en linea para un 80x87 y selecciona
la libreria 80x87. Requiere coprocesador.
IZa
IZc
IZd
IZe
IZg
IZi
IZI
IZp[num]
IZr
IZsjich
compatibilidad ANSI. No permite extensiones Microsoft
ignora mayusculas/minusculas para cualquier nombre de-
clarado con ~ascal. No disponible con la opdon /qc
genera informacion requerida por el depurador SYMDEB
permite extensiones Microsoft
genera funciones prototipo
genera informacion requerida por el depurador Code View
sup rime informacion de libreria por defecto
empaqueta los miembros de estructuras y uniones (num
es 1, 2 0 4)
permite el chequeo de punteros (utilizar solo con /qc)
ejecuta sobre el fichero fuente especificado un chequeo s610
de la sintaxis
IF bytes
IHnum
llink
IVstring
permite especificar el tamafio de la pila en hexadecimal
numero de caracteres significativos para un nombre. Por
defecto es 31
para crear un fichero ejecutable en un modo compatible
(OS/2)
invoca al enlazador incremental IUNK. Opcionalmente
se pueden afiadir durante el enlace un total de num bytes
de relleno, con el fin de preyer espacio para pequefias mOo
dificaciones.
especificar opciones 0 librerias para el enlazador
copia la version string en el fichero objeto
lAC
IAH
IAL
lAM
IAS
IAT
utiliza el modelo compacta (compact)
utiliza el modelo enorme (huge)
utiliza el modelo grande (large)
utiliza el modelo medio (medium)
utiliza el modelo pequeno (small)
utiliza el modelo pequenito (tiny)
10
lOa
10d
10e
109
10i
101
lOp
10s
lOt
lOw
velocidad de ejecuci6n mayor. Igual que lOt
no "alias" (varios nombres para referirse a una posici6n
de memoria)
eliminaci6n, a nivel de bloque, de subexpresiones identi-
cas con el fin de que su evaluaci6n se produzca una sola
vez
no permite optimizaciones
utilizaci6n de los registros de la UCP para colocar las va-
riables y subexpresiones que se utilizan con mas frecuen-
cia
igual que 10c, pero a nivel de funci6n
genera funciones intrinsecas (mas rapidas)
actua sobre bucles para que se ejecuten mas rapidos
evitar las diferencias en precisi6n cuando se trabaja con
valores reales de tipos diferentes ya que pueden afectar a
las operaciones de relaci6n.
tamano mas pequeno del c6digo generado
velocidad de ejecuci6n mayor. Opci6n por defecto
no "alias" excepto en llamadas a funciones. No disponi-
ble con la opci6n Iqc
lOx
10z
optimizacion equivalente a IOecilgt/Gs
optimizacion maxima sobre bucles y utilizacio
de 10s registros de la UCP
I Fejich
IFl[fich]
I Fm[fich]
I Fojich
IFR[fich]
genera un listado en ASM (fichero .asm).
No disponible con la opcion Iqc
genera un listado combinado fuente/asm (fie
No disponible con la opcion Iqc
nombra el fichero ejecutable (.exe)
genera un listado en codigo objeto (fichero .
No disponible con la opcion Iqc
genera un fichero .map (mapa de enlace)
nombra el fichero objeto (.obj)
genera un fichero extendido .sbr para el analis
go fuente (source browser)
genera un fichero estandar .sbr para el analisis
fuente (PWB source browser)
genera un listado del codigo fuente (fichero
No disponible con la opcion Iqc
genera un fichero .erf (referencias cruzadas)
IEP
IIdir
IP
IUid
lu
Ix
hace que los comentarios no sean suprimidos. Es valida
solamente con las opciones IE, IP y IEP
define una constante simb6lica para el preprocesador. El
valor de id es v. El valor de id puede ser vacio (lDid=)
o 1 (lDid)
copia la salida producida por el preprocesadoT en la sali-
da estandar, insertando la directriz # line.
Esta opci6n suprime la compilaci6n.
igual que IE pero no inserta la directriz # line
afiade el directorio dir a la lista de directorios donde de-
ben buscarse los ficheros .h
igual que IE,pero la salida se copia en un fichero .i
borra la definici6n del identificador id especificado
borra todos los identificadores predefinidos
ignora la variable INCLUDE=
ISlnum
ISpnum
ISscad
IStcad
define el nOde caracteres por linea (por defecto 79)
define el nO de lineas por pagina (por defecto 63)
especifica un subtitulo para el listado fuente
especifica un titulo para el listado fuente
invoca a CIL.EXE (paso 1 del compilador modelo gran-
de). path especifica el camino del fichero CIL. Utilizar esta
opci6n para compilar programas que generan el error' 'out
of near heap space"
IB2path
IB3path
IBATCH
Ic
IHELP
IJ
IMAopc
Inologo
Iqc
IW
IWO
IWn
invoca a C2L.EXE (OS/2)
invoca a C3L.EXE (OS/2)
suprime la salida al ejecutar ficheros .bat
solo compila, no enlaza
peticion de ayuda
en Microsoft C el tipo char es signed por defecto. Estaop-
cion 10 cambia a unsigned, excepto cuando un valor ha
sido declarado explicitamente signed
pasa la opcion ope especificada al MASM
suprime el logotipo (informacion inicial)
compilacion nlpida. Conjuntamente con esta opci6n se
pueden utilizar las opciones IGi (implica ILi) y /Zr. No
se pueden utilizar las opciones: lOw, IGm, IFa, IFc, IFl,
IFs, IFPa, IFPc, IFPc87, IHnum y IZc
indica que jich es un fichero .asm independientemente de
su extension
indica que jich es un fichero .c independientemente desu
extension
igual que IW1. Es la opcion por defecto
suprime los mensajes de aviso del compilador (warnings)
indica el nivel de mensajes de error a visualizar. n esun
valor 1, 2, 3, 0 4. A mayor nivel, mayor exigencia en el
amilisis sintactico durante la compilacion
es el mayor nivel de mensajes de error. Cualquier avisoes
considerado como un error fatal.
igual que WO
sintaxis de la orden emitida (ejemplo cl 17)
Esta orden permite enlazar el fichero objeto obtenido como resultado de
una compilacion, con las librerias apropiadas, para crear un fichero ejecu-
table. La sintaxis de esta orden es la siguiente:
indica uno 0 mas ficheros objetos que se quieren enlazar. Los
nombres de los ficheros objetos son separados por el signo +
o por espacios. La extension asumida por omision es .obj.
es el nombre del fichero ejecutable que se quiere crear. La ex-
tension asumida por omision es .exe.
es el nombre del fichero 0 mapa de carga, el cual contiene una
entrada para cada segmento de los modulos de entrada. Cada
una de las entradas muestra tambit~n el desplazamiento dentro
del fichero ejecutable. Este fichero no se crea a menos que se
solicite especificamente. Si no se introduce nada el nombre oe
fichero reservado es NUL que indica que no se creara ningun
mapa.
Se puede especificar tambien un nombre de dispositivo para di-
rigir la salida a este, como por ejemplo: AUX para un dispositi-
vo auxiliar, CON para la consola (terminal), PRN para la im-
presora.
lib indica una 0 mas librerias (stand-alone) 0 directorios donde pue-
den ser localizadas separadas por signos + 0 por espacios.
opciones pueden ser cualquiera de las siguientes (la parte incluida entre
corchetes es opcional):
IBA IBA[TCH]
Suprime la salida por pantalla de mensajes, dellogotipo, de pre-
guntas 0 de las lineas de un fichero .bat.
Ica ICO[DEVIEW]
Prepara un fichero ejecutable para depurarlo utilizando el de-
purador de Microsoft Code View. Los ficheros objeto enlaza-
dos con esta opcion deben compilarse primero con la opcion IZi.
ICP ICP[ARMAXALLOC]:num
Maximo numero de parrafos de 16bytes necesarios para cargar
el programa en memoria. Por defecto DOS asigna el maximo
espacio disponible no quedando espacio para ejecutar otro
programa.
DO IDO[SSEG]
Solamente para programas en ensamblador. Los segmentos son
ordenados en el fichero ejecutable en un orden preestablecido.
Opci6n por defecto.
IDS IDS [ALLOCATE]
Solamente para programas en ensamblador. Carga todos los da-
tos definidos para estar en la parte mas alta del segmento de
datos. Por defecto, link carga los datos comenzando por la par-
te mas baja del segmento de datos. Esta opci6n es utilizada nor-
malmente con la opci6n IHI.
IE IE [XEPACK]
esta opci6n elimina secuencias repetidas de bytes (generalmen-
te caracteres nulos) y optimiza la tabla de carga, tabla de refe-
rencias relativas al principio del programa. Los ficheros .exeerea-
dos con esta opci6n pueden ser mas pequefios y cargados mas
rapidamente.
'.' I
f", .,
,-;~t~;Y
•••••• u,tI
IF [ARCALLTRANSLATION]
Puede mejorar la velocidad y reducir el tamafio de los progra-
mas. Convierte las llamadas -far a funciones que residen en
el mismo segmento, a llamadas _near. Normalmente se utiliza
con la opci6n IPACKCODE.
IHE[LP]
Petici6n de ayuda.
IHI[GH]
hace que el cargador coloque el fichero ejecutable 10 mas alto
po sible en la memoria. Esta opci6n se utiliza normalmente con
la opci6n IDS.
IINC[REMENTAL]
Invoca al compilador incremental IUNK. No es compatible con
las opciones IE y ITINY. Con esta opci6n se generan dos fi-
cheros mas: .sym y .i1k, necesarios para IUNK.
IINF[ORMATION]
Visualiza la fase de enlace y los nombres de los ficheros objetos
que estan siendo enlazados.
ILl [NENUMBERS]
Crea un fichero .map que incluye los numeros de linea del fi-
chero fuente y las direcciones asociadas. Para ello es necesario
que el fichero 0 ficheros objeto incluyan numeros de linea (op-
ciones del compilador IZi 0 IZd), de 10 contrario la opcion ILl
sera ignorada.
IM[AP]
Crea un fichero que contiene una lista clasificada de todos los
simbolos globales definidos en el fichero 0 ficheros objeto, ade-
mas de la lista de los segmentos del programa en su orden de
aparicion en el modulo de carga.
INOD [EFAULTLlBRARYSEARCH] [:lib]
Ignora las librerias por defecto. Si se especifica lib, LlNK bus-
ca en todas las librerias excepto en esta.
INOE[XTDICTIONARY]
esta opcion previene al programa enlazador de buscar en el dic-
cionario que el mismo mantiene. Normalmente el enlazador con-
sulta este diccionario (lista de localizaciones de simbolos) para
acelerar la busqueda de librerias. La opcion INOE la utilizare-
mos cuando exista una redefinicion de un simbolo.
INOF[ARCALLTRANSLATION]
Si la conversion de llamadas -far a llamadas _near esta acti-
va, entonces se desactiva. (Ver opcion IF).
INOI[GNORECASE]
hace que el enlazador distinga entre letras mayusculas y mi-
nusculas.
INOL[OGO]
Suprime el logotipo.
INON[ULLSDOSSEG]
Clasificar los segmentos en el fichero ejecutable en el mismo
orden que 10 hace la opcion IDO, pero sin bytes adicionales al
comienzo del segmento _TEXT.
ISE ISE[GMENTS]:num
Indica el numero maximo de segmentos 16gicos que un progra·
ma puede tener (por defecto 128). num es un valor de 1 a 16384.
INOP INOP[ACKCODE]
Si el empaquetamiento de los segmentos de c6digo esta activo,
se desactiva.
10 10 [VERLAYINTERRUPT] :number
Permite seleccionar un numero de interrupci6n diferente aI 63
(numero por defecto) para pasar el control a overlays.
IPACKC IPACKqODE][:num]
Empaqueta los segmentos de c6digo en los modelos de memo-
ria medio, grande y enorme. num indica el tamafio maximo para
el nuevo segmento (por defecto 64K). Cuando LINK no pueda
afiadir otro segmento por superar el valor num forma otro nue-
vo. Esta opci6n se utiliza junto con la opci6n IF.
IPACKD IPACKD[ATA][:num]
Empaqueta los segmentos de datos en los modelos de memoria
compacto, grande y enorme.
IPADC IPADqODE]:num
Afiade bytes de relleno al final de cada m6dulo de c6digo para
posteriormente enlazarlos con ILINK. Esto es necesario para
prever espacio para pequefias modificaciones. num especifica
el numero de bytes de relleno (por defecto 40).
IPADD IPADD[ATA]:num
Afiade bytes de relleno en cada segmento de datos.
IPAU IPAU[SE]
indica al enlazador que haga una pausa antes de escribir eIfi·
chero .exe en un disco flexible. Esto per mite insertar un nuevo
disco para almacenar el fichero .exe.
IQ IQ[UICKLIB]
Produce una libreria Quick para titilizarla con Microsoft QuickC
o Microsoft QuickBASIC.
Un numero bajo de segmentos permite un enlace mas rapido
y una asignaci6n de espacio menor.
1ST IST[ACK]:num
especifica el tamafto de la pila para el programa. num es cual-
quier valor positivo (decimal, octal 0 hexadecimal) no superior
a 65535. El tamafto por defecto es de 2K.
IT IT[lNY]
Indica al enlazador que se va a crear un fichero .com. Esta op-
ci6n es incompatible con la opci6n IINCREMENTAL.
Este ejemplo enlaza los ficheros prog.ob}, modI.ob} y mod2.ob}, con
las librerias por defecto (librerias del sistema). El resultado es el fichero
ejecutable prog.exe, almacenado en el directorio source y optimizado en
tamafto y velocidad (opciones IE y IF). Tambien se crea el mapa de carga,
prog.map, correspondiente.
En el siguiente ejemplo, se enlazan los ficheros progJ.ob}, prog2.ob}
y prog3.ob} utilizando la libreria libutil.lib. El resultado es el fichero deno-
minado progI.exe por defecto. El mapa de carga correspondiente es Hama-
do mapaen.map.
Una segunda forma de enlazar uno 0 mas programas es ejecutando la or-
den LINK sin parametros:
Object Modules [.OBJ]:
Run File [nombre-base.EXE]:
List File [NUL.MAP]:
Libraries [.LIB]:
Definitions File [NUL.DEF]:
La primera pregunta se responde con el nombre del programa 0 pro-
gramas a enlazar separados por signos + 0 por espacios. Se asume la ex-
tension .obj. A la segunda pregunta se responde con Enter a no ser que
deseemos cambiar el nombre para el fichero ejecutable. Si deseamos obte-
ner un listado del mapa de carga (fichero .map), debemos responder a la
tercera pregunta con un nombre diferente a NUL; la extension .map se asume.
A la cuarta pregunta se responde con una 0 mas librerias (stand-alone) 0
con nombres de directorios don de pueden ser localizadas, separadas par
signos + 0 por espacios. Por omision se asumen las librerias del sistema.
La quinta pregunta solamente es requerida par OS/2 y Windows, por 10
que simplemente pulse Enter.
Si hay mas nombres que los que entran en una linea, se pone un signa +
al final de esta y se continua en la linea siguiente.
El siguiente ejemplo ilustra como continuar en la linea siguiente utili-
zando el signo +. Este ejemplo enlaza los ficheros .obj especificados y crea
un fichero .exe. Por responder con un punto y coma a la pregunta Run File,
el fichero ejecutable es nombrado par defecto progl y se asumen la res-
puesta NUL.MAP y las librerias .LIB del sistema para las dos preguntas
siguientes.
Object Modules [.OBJ]: progl prog2 prog3 +
Object Modules [.OBJ]: prog4
Run File [PROGl.EXE]: ; <Enter>
A menudo es conveniente salvar las respuestas alas preguntas formulada
por la ejecucion de la orden LINK para utilizarlas posteriormente. Esto
es especialmente uti! cuando hay que especificar listas largas de m6dulos
objeto y ademas se preve, por los motivos que sean, que el proceso de en-
lace, seguramente, habra que realizarlo mas de una vez.
Antes de utilizar esta opci6n debe crearse un fichero de respuesta auto-
matica. Este contiene varias lineas de texto, cada una de las cuales es la
respuestaa una pregunta del enlazador. Estas respuestas deben ir en el mismo
orden que las preguntas del enlazador y siguen las mismas reglas expuestas
anteriormente.
Para especificar un fichero de respuesta automatica se procedera como
se indica a continuaci6n:
a+b+c+d
/PAUSE IMAP
list
xlib.lib;
Esta orden Ie indica al programa enlazador que procese los ficheros
objeto a, b, c y d para producir un fichero ejecutable Hamado a.exe y un
mapa list.map. La opci6n IPAUSE indica a LINK que haga una pausa an-
tes de escribir el fichero ejecutable para poder cambiar el disco flexible,
siesnecesario. La opci6n IMAP indica que se cree el mapa de carga para
el fichero ejecutable resultante de la operaci6n de enlace. Tambien se pro-
duce el enlace con las rutinas necesarias de la libreria x/ib./ib.
Sino se tiene la seguridad de que el programa coja en la memoria disponi-
blepodemos crear overlays. Un overlay es una parte del programa (m6du-
10) almacenada sobre el disco; la cual sera cargada en memoria cuando se
requiera su ejecucion. Para especificar que un determinado modulo seva
a tratar como un overlay se incluye entre parentesis.
Este ejemplo crea prog.exe en el directorio actual de trabajo con dOl
overlays: (modl+mod2) y (mod3).
La utili dad IUNK (enlazador incremental) normalmente es invocada por
los compiladores QuickC y PWB basados, por defecto, en la compilaci6n
y enlace incremental (opciones IGi 0 ILi). No obstante, IUNK tambien
puede ejecutarse directamente desde DOS.
Para poder ejecutar IUNK previamente debe utilizarse, al menos una
vez, la orden LINK con las opciones IINC, IPADC y IPADD; ya que lUNK
trabaja sobre un fichero .exe existente (no .com), el cual desearn os modificar.
fich.exe es el nombre del fichero ejecutable que se quiere crear. La ex-
tension asumida por omision es .exe.
objeto indica uno 0 mas ficheros objetos que se quieren enlazar sin
la extension .obj y separados por espacios.
IA indica a IUNK que verifique si han ocurrido cambios en 101
ficheros .obj desde el ultimo proceso de enlace.
IE "orden!; orden]..."
inqica la orden u 6rdenes que deben ser ejecutadas si ILINK
falla. La opd6n por defecto es IE "LINK IINC".
si ocurre un fallo al ejecutar ILINK, por defecto, no se invoca
LINK lINe.
IX si esta disponible un manejador de memoria expandida, esta
opd6n hace que ILINK no 10 utilice. Por defecto, ILINK uti-
liza memoria expandida.
LIBRERIAS Y
UTILIDADES DEL COMPILADOR
Una de las caracteristicas mas importantes de C es la facilidad con la que
se pueden construir y utilizar librerias de funciones. Una libreria es una
coleccion de funciones precompiladas y agrupadas bajo un mismo nom-
bre(fichero) que pueden ser utilizadas por cualquier programa. La ventaja
de utilizar tales librerias esta en no tener que escribir el codigo fuente de
la funcion (de libreria) en el programa y en el tiempo que se ahorra, ya
queel compilador cuando compila nuestro programa no tiene que compi-
lar tales funciones.
C proporciona la utilidad LIB para crear librerias que llevan como ex-
tension.LIB y para mantener las ya existentes. Estas son denominadas tam-
bien librerias stand-alone 0 librerias independientes (cuando son requeri-
das el sistema las llama automaticamente). Estas librerias tienen el mismo
formate que las suministradas con C y con otros lenguajes de alto nivel
de Microsoft.
En este capitulo, ademas de las librerias, veremos otras utilidades como
NMAKE y Code View por citar algunas de ellas.
Si se escribe un punta y coma (;) a continuacion del nombre de
la libreria, Lffi ejecuta solamente un chequeo sobre la libreria
especificada. Si se encuentra algun modulo invalido se escribe
un mensaje de error. Este chequeo normal mente no es necesa-
rio, ya que Lffi cheque a cada fichero objeto antes de afiadirlo
a la libreria. Por ejemplo:
EI manejador de librerias Lffi permite crear y mantener librerias .LIB. Un
programa que llama a rutinas de una libreria es enlazado (link) con la Ii-
breria para producir el fichero ejecutable. Solamente son enlazadas las ru-
tinas necesarias, no todas las rutinas de la libreria.
Una libreria stand-alone esta hecha a base de modulos objeto que han
sido combinados para formar la misma. A diferencia de un fichero objeto,
un modulo objeto no existe independientemente de la libreria a la que per-
tenece y no tiene un camino 0 extension asociada con su nombre.
• Extraer modulos de una libreria existente y colocarlos como fiche-
ros .obj independientes.
• Combinar el contenido de dos librerias existentes para formar una
unica libreria.
Iib-vieja indica el nombre de la libreria que se desea crear 0 modificar.
Si la libreria no existe, LIB pregunta si se quiere crear. Por de-
fecto se asume la extension .LIB.
IH[ELP]
peticion de ayuda.
II [GNORECASE]
indica a LIB, cuando compara nombres, que no haga distincion
entre minusculas y mayusculas. Utilizar esta opcion cuando se
combine una libreria creada con la opcion INOI, con otras que
no han sido creadas con esta opcion. La libreria resultante no
es marcada INOI. Es la opcion por defecto.
INOE[XTDICTIONARY]
indica a LIB que no genere un diccionario. Este es una parte ex-
tra de la libreria que mas tarde dara lugar a una mayor veloci-
dad en el proceso de enlace de la misma. Utilizar esta opcion
cuando se presente el error out of memory. Otra posibilidad es
dividir la libreria en otras mas pequefias.
INOI[GNORECASE]
indica a LIB, cuando compara nombres, que distinga minuscu-
las y mayusculas. Utilizando esta opcion, nombres como fUDcA
y fUDca pueden ser puestos en la misma libreria.
Cuando se crea una libreria con esta opcion, LIB marca la libre-
ria internamente para indicarlo. Cuando se combinan varias 1-
brerias y una de ellas esta marcada INOI, la libreria resultante
es marcada tambien INOI.
INOL[OGO]
suprime el logotipo en la salida.
IPAGESIZE:n
especifica el tamafio de la pagina para la libreria. n es una po-
tencia entera de 2 cuyo valor esta comprendido en el rango de
16 a 32768. Por defecto el tamafio de la pagina es de 16 bytes.
El tamafio de la pagina afecta a la alineacion de los modulos
en la libreria. Los modulos en la libreria son siempre alineados
para comenzar en una posicion que sea un multiplo del tamafio
de la pagina.
+ {fich-obj/lib J afiade a la libreria el fichero objeto especificado. Si la ex-
tension no se especifica se asume .obj. Si se especifica una
libreria, su contenido es afiadido a la libreria Iib-vieja.
El nombre de la libreria debe tener la extension .lib.
-+modulo reemplaza en la libreria el modulo especificado borran·
do el modulo y sustituyendolo por el fichero objeto del
mismo nombre.
*modulo copia el modulo especificado a un fichero objeto del mis-
mo nombre.
-*modulo mueve el modulo especificado a un fichero objeto del mis·
mo nombre.
nombre del fichero de referencias cruzadas. Si no se especifica
no se crea. Este fichero contiene:
1. Una lista alfabetica de todos los simbolos globales en la Ii-
breria. Cada simbolo va seguido por el nombre del modulo
en el que esta contenido.
2. Una lista de los modulos de la libreria. Esta lista incluyeel
nombre, el desplazamiento en el fichero, y el tamafio de cada
modulo. El nombre de cada modulo va seguido por una lis·
ta alfabetica de los simbolos glob ales definidos en el mismo.
lib-nueva es el nombre de la libreria modificada que LIB crea como sali·
da. Cuando se especifica esta opcion la libreria original perma·
nece sin modificar. Si no se especifica se crea una copia de segu·
ridad de la libreria original con extensi6n .bak, permaneciendo
con el mismo nombre la modificada.
Reemplazar el m6dulo fund en la libreria lib_a. lib por el fichero ob-
jeto del mismo nombre funcl.ob}.
EI mismo resultado se obtendria con cualquiera de las dos 6rdenes si-
guientes,ya que las operaciones de borrado se efectuan siempre antes que
las operaciones de anadir, indiferentemente del orden en el que se hayan
especificado en la linea de 6rdenes.
EI siguiente ejemplo mueve el m6dulo fund de la libreria lib_a. lib
a un fichero objeto denominado fund.ob}, con 10 que este modulo desa-
parecede la libreria. Copia el m6dulo func2 a un fichero objeto denomi-
nadofunc2.ob}, este m6dulo permanece en la libreria. La libreria modifi-
cadaes Hamada lib_b. lib permaneciendo sin cambiar la libreria lib_a. lib.
LIB lib_a -fund +fund;
LIB lib_a +fund -fund;
Library Name:
Library does not exist. Create? (y/n)
Operations:
List File:
Output .library:
La segunda pregunta s610nos es formulada cuando no existe la libre-
ria especificada. La quinta s610nos es formulada si existe la libreria espe-
cificada.
Si a alguna de estas preguntas, excepto a la primera, se responde con
Enter, se asume para ella la respuesta por defecto y si se responde con un
punto y coma se asume para ella y para las siguientes las respuestas por
defecto.
La respuesta por defecto para List File es NUL y para Output library
es la especificada en Library Name.
Igual que se indic6 para la orden LINK, para la orden LIB se puede crear
un fichero de respuesta automatica y especificar la orden:
Creamos el fichero res.Ibr con las siguientes respuestas, suponiendo
que deseamos crear la libreria especificada:
milib
y
+mayor +cambiar
El siguiente ejempl0 crea una libreria Hamada milib.lib con 10s m6dulos
mayor y cambiar correspondientes a 10sficheros mayor.obj y cambiar.obj.
Los pasos a seguir son 10s siguientes:
/* Mayor de x, y, Z
*/
float mayor(f1oat x, float y, float z)
[
float ma;
if(x>y)
if ( x > z)
ma x;
else
ma = z;
else
if (y > z)
ma = y;
else
ma = z;
return ma;
/ * Perm uta 10s valores de x e y
*/
void cambiar(f1oat ~, float *y)
[
float awe;
aux = ~;
~ *y;
*y = awe;
Library name: milib
library does not exit. Create? (y/n) y
Operations: +mayor +cambiar
List file: ;
1* miprog.c
*1
# iclude <stdio.h >
# include Hmisjuncs.h"
main( )
(
float a, b = 1, c = 2;
a = mayor(l, 2, 3);
cambiar(&b, &c);
printj(H%g %g %g': a, b, c);
}
float mayor(f1oat, float, float);
void cambiar(f1oat *, float *);
7. Compilar miprog.c y enlazarlo can la libreria milib.lib para obtener
el programa ejecutable, miprog.exe.
NMAKE es una utilidad que automatiza el mantenimiento de programas.
En lugar de escribir explicitamente las ordenes de compilar y enlazar, es
posible escribir todos 10s pasos necesarios para obtener un fichero ejecuta-
ble en un fichero de texto denominado por defecto makefile, que sera in-
vocado mediante la orden NMAKE. Esto es particularmente util para cons-
truir programas desde varios modulos fuente. NMAKE solo recompilaria
los modulos fuente que hayan sufrido modificaciones en base a la fecha
de la ultima vez que se modifieD el fichero, 10 cual exige introducir la fecha
correctamente al iniciar cada sesion.
Cuando se ejecuta NMAKE sin un nombre de fichero, se busca uno deno-
minado makefile. Si no existe, hay que especificar explicitamente el fichero
que deseamos utilizar como argumento. El fichero al que nos referimos,
"fichero de descripciones", describe las acciones necesarias para construir
un determinado programa. Consta de uno 0 mas bloques y cad a bloque
esta formado por una linea de dependencia seguida por una 0 mas Iineas
de ordenes. El formate es el siguiente:
fich-'esultante ... :[[path; ... }][fichl_dependiente .. .][,·orden][#comentario}
[ordenl}
[# comentario]
[# comentario] I [orden2]
La primera linea (linea de dependencia) tiene que comenzar en la co-
lumna 1 y el sangrado de al menos un espacio en las line as de ordenes es
necesario. En ella se especifican los jicheros resultantes y 10s fieheros de
los que estos dependen, jicheros dependientes. Opcionalmente se pueden
especificar los caminos de cada uno de losjicheros dependientes entre lla-
ves y separados por puntos y comas.
Un fichero es dependiente si cuando cambia causa una modificacion
en 10s ficheros que se derivan de el.
Una orden es cualquier expresi6n valida que pueda ejecutarse desde
DOS. Hay tres modificadores especiales que pueden utilizarse como prefi-
jo de cualquier orden:
ignora un posible error en la ejecuci6n de la orden. NMAKE conti-
mla el proceso y no se detiene.
repite la orden para cada fichero dependiente si dicha orden utiliza
alguna de las macros especiales $(?) o· $(**).
Un fichero de descripciones puede ser documentado con comentarios.
Un comentario aparecera como:
NMAKE ignora el texto que hay des de el simbolo # hasta el final de
la linea. Un comentario puede colocarse solo en una linea a partir de cual-
quier columna, excepto en la secci6n de lineas de 6rdenes que debe comen-
zar en la columna 1; al final de cualquier linea del fichero, excepto en las
lineas de 6rdenes 0 a partir de la columna 1 de una linea de 6rdenes sepa-
rado de la orden por el caracter I (ASCII 124).
# progl60J.mak
progI60J.exe: progl601.obj # Linea de dependencia
# Linea de 6rdenes
# Enlace I CL -nologo progl60J.obj
progI60J.obj: progl60J.c # Linea de dependencia
# Linea de 6rdenes
# Compilaci6n I CL -c -nologo progl60J.c
primero chequea si la fecha de progl60J.c es mas reciente que prog1601.obj.
Si es mas reciente, 0 si progl60J.obj no existe, ejecuta la orden:
Esta orden crea un fichero progl601.obj nuevo, el cual es ahora mas
reciente que progI60J.exe, 10 que hace que se ejecute la orden:
Observar que la construcci6n del fichero de 6rdenes parte siempre del
fichero resultante.
A continuaci6n presentamos el contenido de un fichero miprog.mak
que construye el fichero miprog.exe a partir de los m6dulos fuente: miprog.c,
mayor.c y cambiar.c descritos en el apartado anterior.
# miprog.mak
miprog.exe: miprog.obj mayor.obj cambiar.obj # bloque de enlace
CL miprog.obj mayor.obj cambiar.obj
miprog.obj: miprog.c # compi/ar
CL -c -nologo miprog.c
mayor.obj: mayor.c # compi/ar
CL -c -nologo mayor.c
Cambiar.obj: cambiar.c # compi/ar
CL -c -nologo cambiar.c
opciones se utilizan para ejecutar NMAKE bajo unas condiciones espe-
cificas.
macro_definiciones
lista de definiciones de macros. Las definiciones de las macros
que contengan espacios en blanco han de incluirse entre comi·
llas dobles.
ficheros_resultantes
lista de 10s ficheros a construir. Si no se especifica, NMAKE
construye el primer fichero resultante especificado en el fiche-
ro de descripciones.
IA construye todos los ficheros resultantes aunque los ficheros de-
pendientes hayan sufrido modificaciones, 0 no.
Ie suprime los mensajes de derechos de co pia, errores no fatales
yavisos.
visualiza las 6rdenes a ejecutar por NMAKE pero sin ejecu
tarlas. Se utiliza como chequeo.
no permite la redefinici6n de macros heredadas. NMAKE he·
reda todas las variables de entorno como macros 10 que puede
causar redefiniciones en el fichero de descripciones.
indica el nombre del fichero de descripciones a utilizar. Si no
se especifica se supone el fichero makefile.
ignora el c6digo de salida (error level). Si no se especifica, cual-
quier programa que retorne un c6digo distinto de cera, provo-
ca la finalizaci6n de NMAKE:
visualiza todas las macros, reglas de inferencia y ficheros re-
sultantes.
chequea si hay ficheros que han variado respecto al fichero re-
sultante; en caso afirmativo retorna un valor distinto de cero.
En caso contrario devueve un O.
suprime la visualizaci6n de las 6rdenes del fichero de descrip-
ciones cuando son ejecutadas.
cambia las fechas de los ficheros que han variado a la fecha
actual.
Las macros se utilizan para crear un fichero de descripciones valida para
varios proyectos. Por ejemplo las macros pueden representar nombres de
ficheros en 6rdenes. Esos nombres pueden entonces ser definidos cuando
se ejecute NMAKE. Las macros pueden tambien utilizarse para controlar
las opciones que NMAKE pase al compilador y al enlazador. Una macro
se crea de la forma siguiente:
Para utilizar una macro en un fichero de descripciones emplearemos
la siguiente sintaxis:
Si el nombre de la macro es de un s610caracter 10sparentesis no son
necesarios.
FUENTES = MODJ.C MOD2.C MOD3.C
PROG.EXE: $(FUENTES: .C=.QBJ) # /{nea de dependencia
LINK $**;
Este ejemplo describe una macro Hamada FUENTES. Con esta ma-
cro sustituimos en la linea de dependencia la extension .C por la extension
.OBJ. De este modo NMAKE ejecuta la siguiente orden:
NMAKE provee varias macros predefinidas. Algunas de eHas son las si-
guientes:
nombre del fichero resultante que NMAKE esta procesan-
do. Esta macro solamente es utilizada en la linea de depen-
dencia para especificar una dependencia.
lista de los ficheros dependientes que han variado con res-
pecto al fichero resultante.
fichero dependiente que ha variado con respecto al fichero
resultante. Esta macro solamente es utilizada en reglas de
inferencia.
orden utilizada para invocar al compilador Microsoft C.
Por defecto CC = CL.
orden utilizada para invocar al Macro Ensamblador. Por
defecto AS = MASM.
orden utilizada para invocar recursivamente a NMAKE. Por
defecto MAKE = NMAKE.
$(MAKEFLAGS)
opci6n de NMAKE actualmente en efecto. Para invocar re-
cursivamente a NMAKE utilizar:
$(MAKE) -$(MAKEFLAGS).
MILIB.LIB: MODl.oBJ MOD2.oBJ MOD3.oBJ
!LIB MILIB.LIB -+$?,o
La macro $? representa el nombre de todos los ficheros dependientes
que han variado con respecto al fichero resultante. EI simbolo !que prece-
de a la orden LIB hace que NMAKE ejecute la orden LIB una vez por cada
fichero dependiente que ha variado.
Los caracteres D, F, By R pueden modificar el valor de algunas de las ma-
cros anteriores segun se indica a continuaci6n:
DIR = C:  C600 INCLUDE
$(DIR)  GWBALS.H $(DIR)  TYPES.H $(DIR) MACROS.H· $$(@F)
COpy $? $@
La macro especial $$(@F) indica el nombre (sin path) del ficherore-
sultante actual. NMAKE primero chequea si GLOBALS.H, en el directo-
rio actual, ha variado con respecto al fichero resultante C:  C600 INCLU-
DE  GWBALS.H Si ha variado ejecuta la orden COPY. El proceso serepite
para el resto de los ficheros.
Estos ultimos ejemplos demuestran como NMAKE puede utilizarse
para realizar otros trabajos diferentes al de mantenimiento de programas.
Las reglas de inferencia son plan tillas desde las cuales NMAKE decideque
hacer con un bloque de descripci6n cuando no contiene ficheros depen·
dientes u 6rdenes. Una regIa de inferencia se define de la siguiente forma:
orden representa las 6rdenes para construir los ficheros con exten·
si6n a_ext desde los ficheros con extensi6n desde_ext.
caracter afiadido
D F B R
camino (path)
nombre base
extensi6n
si no no si
no si SI SI
no si no no
f{desde-path J}.desde_extf{ a-path J}.a_ext:
orden]
.C.OBJ:
CL -c $<
La macro $< representa el nombre de un fichero dependiente que ha
variado con respecto al fichero resultante.
Cuando NMAKE encuentra una descripcion de un bloque sin orde-
nes busca, apoyandose en Ias extensiones dadas, una regIa de inferencia
que especifique como crear el fichero resultante desde Ios ficheros depen-
dientes. Si no existe un fichero dependiente, NMAKE busca una regIa de
inferencia que especifique como crear Ia depend encia desde otro fichero
con el mismo nombre base.
# RegIa de inferencia
.oBJ.EXE:
LINK $<;
# Bloque 1
PROGOI.EXE: PROGOI.OBJ
# Bloque 2
PROG02.EXE: PROG02.oBJ
LINK ICO PROG02, , , MILIB.LIB;
En este ejempIo, Ia regIa de inferencia es aplicable a ficheros depen-
dientes con extension .oBi y ficheros resultantes con extension .EX£. Como
en el bloque 1 no hay ordenes NMAKE aplica Ia regIa de inferencia reem-
plazando $< por PROGOI.OBJ. En cambio.' como en el bloque 2 hay or-
denes NMAKE no aplica Ia regIa de inferencia.
Las reglas de inferencia solamente son titiles cuando existe una corres-
pondencia, uno a uno, entre ficheros con extension desde_ext y ficheros
con extension a_ext. Las reglas de inferencia eliminan Ia necesidad de po-
ner Ias mismas ordenes en varios bloques.
Las reglas de inferencia pueden especificarse en un fichero de descrip-
ciones, en un fichero a incluir (!INCLUDE) 0 en el fichero TOOLS.INI.
NMAKE utiliza las sig~ientes reglas de inferencia predefinidas:
.C.OBJ
.C.EXE
.ASMOBJ
$(CC) $(CFLAGS) Ie $*.C
$(CC) $(CFLAGS) $*.C
$(AS) $(AFLAGS) $*;
CL Ie $*.C
CL $*.C
MASM $*;
Las directrices permiten ejecutar 6rdenes condicionalmente, visualizar men-
sajes de error, incluir el contenido de otros ficheros y actuar sobre algunas
opciones de NMAKE. La sintaxis correspondiente es la siguiente:
!IF expr si la expr es verdad, se ejecutan las sentencias entreel
siguiente !ELSE 0 !ENDIF
!ELSE si la expr es falsa, se ejecutan las sentencias entre !ELSE
y!ENDIF
!IFDEF macro si la macro esta definida, se ejecutan las sentencias en·
tre el siguiente !ELSE 0 !ENDIF.
!IFNDEF macro si la macro no esta definida, se ejecutan las sentencias
entre el siguiente !ELSE 0 !END IF.
CAPITULO 16: LIBRERIAS Y UTILIDADES DEL COMPILADOR 589
!CMDSWITCHES {+ I-lope
activa (+) 0 desactiva (-) la opci6n ope especificada.
ope puede ser ID, II, IN y IS. Antes del signo + 0
- es necesario un espacio.
#prog1602.mak
IINCLUDE <reglas.txt>
ICMDSWITCHES +D
prog1601.exe:prog1601.obj
IIFDEF DEBUG
I IF H$(DEBUGj"= = HS"
LINK ICO prog1601.obj;
I ELSE
LINK prog1601.obj;
I ENDIF
IELSE
I ERROR maero DEBUG no dejinida.
IENDIF
Este ejemplo lee y evalua el fichero reglas.txt. Activa la opci6n ID de
NMAKE, la cual visualiza las fechas de los ficheros chequeados. Comprueba
si la macro DEBUG esta definida y si 10 esta y su valor es "s" invoca al
enlazador con la opci6n Ica y si su valor no es "s" invoca al enlazador
sin la opci6n ICO. Si no esta definida visualiza un mensaje de error y fina-'
liza NMAKE.
NMAKE IF prog1602.mak DEBUG=s
NMAKE prog1602.mak
Si una macro esta definida en mas de un lugar, la ejecuci6n de la misma
se hace en el primer lugar que se encuentre al aplicar el siguiente orden
de prioridades:
1 linea de 6rdenes
2 fichero de descripciones 0 fichero a incluir
3 variables de entorno
4 fichero TOOLS.INI
5 macros predefinidas
Cuando necesitamos emitir una orden con una lista de argumentos que ex-
cede de la longitud maxima impuesta par DOS (127 caracteres), la solu-.
ci6n es generar un fichero en linea. NMAKE puede generar ficheros en li-
nea que serviran como ficheros de respuestas automaticas para otros
programas. Para generar un fichero en linea la sintaxis es la siguiente:
jich-resultante: jich_dependiente .
orden < <Uich_lJ
texto del jichero en llnea
«[KEEP I NOKEEPj
La totalidad del texto especificado entre angulos es colocado en un
fichero en linea nombrado jich_l. Si el nombre se omite se crea un fichero
temporal; en otro casa, el fichero sera temporal si se finaliza con NOKEEP
(opd6n por defecto), 0 permanente si se finaliza con KEEP. En cualquier
caso NMAKE s610crea el fichero si la orden es ejecutada.
miprog.exe: modJ.ob} mod2.ob} mod3.ob} mod4.ob} mod5.ob}
LINK @«
modI +mod2 +mod3 +mod4 +mod5, miprog;
«
Este ejemplo crea un fichero temporal equivalente a un fichero de res-
puesta automatica para la orden LINK.
Con la orden NMAKE se pueden utilizar los caracteres asterisco (*) e inte-
rrogante (?) para especificar ficheros en general. Otros caracteres especia-
les son los siguientes:
simbolo de escape. Es utilizado para que NMAKE interprete
los caracteres especiales que se describen a continuaci6n
como caracteres normales.
La descripci6n de un fichero puede utilizar una sintaxis especial para de-
terminar cada uno de sus componentes. Esta sintaxis es:
OJos representa la especificaci6n completa del primer fichero de-
pendiente.
% IparteF representa una componente. parte es uno de los siguientes ca-
racteres:
p camino (path)
d unidad (drive)
OJos
% IpF
% IdF
%IfF
% IeF
C:  C600  SOURCE  PROG.C
C:  C600  SOURCE
C:
PROG
.C
Un pseudoobjetivo es un objetivo que no es un fichero en un bloque de
descripci6n. Un pseudoobjetivo es un nombre que sirve para construir un
grupo de ficheros 0 ejecutar un grupo de 6rdenes. Por ejemplo:
AL--.DIA: *.C
ICOPY $** A: PROYECTO
Cuando NMAKE evalua un pseudoobjetivo siempre considera que10s
ficheros dependientes han variado. En el ejempl0 anterior, NMAKE copia
todos 10sficheros con extensi6n .C a la unidad y directorio especificados.
ID IF prog.mak 
DEBUG=s
El fichero especial denominado TOOLS.INI tiene para NMAKE la misma
funci6n que el fichero AUTOEXEC.BAT para DOS. Cada vez que se eje-
cuta NMAKE busca en el fichero TOOLS.INI las sentencias asociadas con
la etiqueta [NMAKE] para ejecutarlas y a continuaci6n ejecuta el fichero
de descripciones especificado.
El ejemplo que se presenta a continuaci6n, prog1603.mak, ha side disefia-
do para que trabaje con el soporte del fichero TOOLS.INI; es un buen ejem-
plo para fijar des de el punta de vista pnictico los conceptos expuestos an-
teriormente. La orden de ejecuci6n es de la siguiente forma:
El fichero prog1601.ini es el soporte que nos va a dar el fichero
TOOLS.INI. Contiene las opciones para el compilador, para el enlazador
y para la utilidad LIB, asi como las reglas de inferencia resultantes. De esta
forma quedan definidas las opciones y las reglas de inferencia por defecto.
Por 10 tanto, en 10 sucesivo, asumiremos que dicho contenido ha sido es-
crito en el fichero TOOLS.INI.
# prog160l.ini
# Estas sentencias jormardn parte del jichero TOOLS.INI
!IFDEF DEP
CFLAGS=!Od IZi IW2
LFLAGS=ICO IE INOI
!ENDIF
# Verificar si ha sido dejinida la macro INC para habilitar la
# compilacton y el enlace incremental
!IFDEF INC
CFLAGS=lqc IGi IZr IW2
LFLAGS=IINC INOI
!ENDIF
!IFDEF DEP
! IFDEF INC
! ERROR depuracton y compilacion incremental simultdneamente
! ENDIF
!ENDIF
# Redejinir la regia de injerencia CC dejinida en TOOLS.INI
.c.obj:
!$(CC) $(CFLAGS) $(DEBUG) Ie $<
.c.exe:
!$(CC) $(CFLAGS) $(DEBUG) $< llink $(LFLAGS)
PROYEC16: FUNC16*.C MAINOBJ
$(CC) $(CFLAGS) $(DEBUG) Ie FUNC16*.C
!LIB PROYEC16 $(LIBFLAGS) -+$(**B);
LINK $(LFLAGS) PROYEC16.LIB, $(@B).EXE;
$(@B)
1. Si en la orden NMAKE se ha definido la macro NDEBUG se in-
habilita la macro "assert", definida en <assert.h >, que es utili-
zada can fines de depuraci6n.
!IFDEF DEP
CFLAGS = IOd IZi IW2
LFLAGS=ICO IE INOI
!ENDIF
# Verificar si ha sido dejinida la macro INC para habilitar la
# compi/acion y el enlace incremental
!IFDEF INC
CFLAGS = Iqc IGi IZr IW2
LFLAGS=IINC INOI
!ENDIF
!IFDEF DEP
! IFDEF INC
! ERROR depuracion y compilacion incremental simultcineamente
! ENDIF
!ENDIF
# Redejinir la regIa de injerencia CC dejinida en TOOLS.INI
.c.obj:
!$(CC) $(CFLAGS) $(DEBUG) Ie $<
.c.exe:
!$(CC) $(CFLAGS) $(DEBUG) $ < llink $(LFLAGS)
PROYEC16: FUNC16*.C MAIN.OBJ
$(CC) $(CFLAGS) $(DEBUG) Ie FUNC16*.C
!LIB PROYEC16 $(LIBFLAGS) -+$(**B);
LINK $(LFLAGS) PROYEC16.LIB, $(@B).EXE;
$(@B)
1. Si en la orden NMAKE se ha definido la macro NDEBUG se in-
habilita la macro "assert", definida en <assert.h >, que es utili-
zada con fines de depuraci6n.
2. Si en la orden NMAKE se ha definido la macro DEP se estable-
cen las opciones de compilaci6n y enlace que permitan depurar
el programa.
3. Si en la orden NMAKE se ha definido la macro INC se establecen
las opciones que permitan la compilaci6n y el enlace incremental.
4. Verifica que las macros DEP e INC no hayan sido definidas si-
multaneamente. Esta acci6n se considera como un error.
5. Si en la orden NMAKE hemos definido alguna de las macros an-
teriores, las reglas de inferencia por defecto, seran modific~das con
las nuevas opciones; en otro caso las opciones por defecto perma-
neceran inalteradas.
Por ultimo hemos escrito dos bloques para mantener un supuesto pro-
yecto, PROYEC16, compuesto por una funci6n principal MAINC y un con-
junto de funciones auxiliares FUNC16*.C. El primer bloque genera un fi-
chero PROYEC16.EXE y 10 ejecuta ($(@B)). El segundo bloque compila
la funci6n principal MAIN, utilizando para ello las reglas de inferencia.
El depurador Code View de Microsoft es un programa interactivo que nos
ayudara a localizar errores rapidamente. Code View puede utilizarse sola-
mente con ficheros .exe no se puede utilizar con ficheros .com.
Cuando depuramos un programa es bueno tener presente la siguiente
consideraci6n: el compilador C permite escribir mas de una sentencia en
una misma linea. Por ejemplo:
Esta forma de escribir un program a dificulta la depuraci6n, ya que
no es posible acceder alas sentencias individuales que componen la linea.
Por ello, se recomienda escribir las sentencias anteriores de la siguienteforma:
car = texto[i];
if (car = = < n')
+ +lineas;
Cuando un programa que se desea depurar se compila se debe especificar
la opcion IZi. Esta opcion indica al compilador que incluya numeros de
linea e informacion extra para el depurador en el fichero objeto.
Si en un programa compuesto por varios modulos, solamente desea-
mos depurar algunos de ellos, estos deben ser compilados con la opcion
IZi y el resto con la opcion IZd. La opcion IZd afiade al fichero objeto
numeros de linea, pero no informacion extra, con 10 que el espacio ocupa-
do en el disco y en la memoria es menor. Tambien debe especificarse la
opcion IOd la cual impide la optimizacion, ya que esta puede dificultar
la depuracion.
Este ejemplo compila y enlaza el fichero fuente progOJ.c. EI resultado
es un fichero objeto con informacion para el depurador, que es enlazado
automaticamente con la opcion ICO (abreviatura de ICODEVIEW).
EI siguiente ejemplo compila moduloOJ.c, produciendo un fichero
moduloOJ.obj con la informacion total necesaria para el depurador; com-
pila modulo02.c produciendo un fichero objeto con informacion limitada.
A continuacion se utiliza CL otra vez para enlazar los ficheros objeto re-
sultantes. Esta vez, CL no compila de nuevo, ya que los argumentos no
tienen extension. La opcion IZi en esta ultima orden hace que el enlazador
(linker) sea invocado con la opcion ICO. El resultado es un fichero ejecu-
table, moduloOJ.exe, que permite depurar moduloOJ.c.
CL /Zi IOd Ie moduloOJ.c
CL IZd IOd Ie modulo02.c
CL IZi moduloOI modulo02
Una vez compilado un programa con las opciones necesarias para depu-
rarIo invocaremos a Code View. La sintaxis es la siguiente:
fichero-ejecutable es el nombre del fichero ejecutable que se quiere
depmar.
son los parametros que opcionalmente podemos pa-
sar en la linea de 6rdenes al fichero ejecutable.
Code View utiliza la memoria expandida si EMM esta instalado 0 la
memoria extendida si XMM esta instalado.
Inn inicializa el modo de 25 lineas (nn=25), de 43 lineas (nn=43)
o de 50 lineas (nn = 50).
/C6rdenes permite especificar una 0 mas 6rdenes que se ejecutanin in-
mediatamente despues de invocar a CV.
ID[Kb] ejecuta CV empleando la tecnica de overlays con el fin de dis-
poner de mas espacio para el programa. Kb es un valor (mul-
tiplo de 16)entre 16y 128que especifica el tamafio del overlay.
no permite el intercambio de paginas de video entre CV y el
programa. Opcion por defecto.
permite utilizar el controlador de interrupciones. Esta opcion
permite la utilizacion de Ctrl +C y de Ctrl + Break en ordena-
dores no compatibles con IBM.
inhabilita el raton. Utilizar esta opcion cuando se depure una
aplicacion que soporta raton.
permite utilizar los 4 registros especiales del 80386. Esto da
lugar a una mayor velocidad. Esta opcion no esta soportada
en modo protegido.
permite el intercambio de paginas de video entre CV y el
programa.
Cuando invocamos al depurador Code View (CV nombre_programa)
se visualiza una pantalla dividida en tres areas. El area primera ocupa la
linea 1, la cual visualiza el menu principal.
El area segunda ocupa las lineas 2 a 24 (dependiendo del tipo de tarje-
ta de video podemos disponer de mas lineas) y es utilizada para visualizar
las ventanas del depurador. Para movernos dentro de estas ventanas pode-
mos utilizar las teclas de movimiento del cursor y las teclas PgUp y PgDn.
Para pasar de una ventana a otra se puis a la tecla F6 0 Shift + F6.
El area tercera ocupa la linea 25 y es utilizada para indicar las accio-
nes que podemos to mar.
[BP+flflfI'Il
[BP+flflfl81
[BP+flflflCl
II
If1:
11:
12:
13:
1'1:
IS:
float
float
float
local
x = 1. fIfIfIfIfI
y = 2.fIfIfIfIfI
z = 3.fIfIfIfIfI
irl"••• ···,·;S"' ..·.:i.'WIl
reg
= flfIfI&
= If11A
= flfIBl
= '1f1f1f1
= If11&
= If12f1
SI = fI'I82
01 = fI'I82
OS = 3f1C&
ES = 2BsO
SS = 3f1C&
CS = 2B7D
IP = fllZE
fL = fl2f12
else
Md z:
else
if ( y > Z )
Ma y:
else
Moryl byte OS:fIfIfIfI(ACTIVE)
CO 2f1 C& '1f1fIfI9A ffl fE 10 ffl 8B fl2 fIfI2A 51 fl3
fIfI2A BB flA fIfI2A '1C 2B fll fll fll fIfIfl2 fl3 ff ff
ff ff ff ff ff ff ff ff ff ff ff ff SO 2B A2 2&
t-------------<:coMMand
Las operaciones minimas que debe incluir un depurador son las si-
guientes:
Permite ver la sentencia del programa que es ejecutada. Code View
incluye las siguientes opciones:
Ejecutar una sentencia cada vez, incluidas funciones definidas
por el usuario. Esta modalidad se activa y se continua pulsando
la tecla F8. Si no queremos que las funciones se ejecuten senten-
cia a sentencia pero si la funci6n principal main( ) utilizamos
la tecla FIO.
Un punta de parada es una pausa que se hace en un lugar determi·
nado dentro del programa. Esto permite comprobar los valores de
las variables en ese instante. Colocar los puntos de parada donde
se sospeche que esta el error.
Para poner 0 quitar una pausa se coloca el cursor en ellugar donde
va a tener lugar la pausa y se pulsa F9. Se pueden poner tantos pun-
tos de parada como necesitemos.
Las ventanas local y watch permiten observar los valores de las va-
riables y de las expresiones especificadas. Al ejecutar el programa
paso a paso podemos observar en estas ventanas los valores que van
tomando las variables.
Si pulsamos la tecla F5 la ejecuci6n continua hasta el final del pro-
grama 0 hasta el primer punta de parada, si existe.
Cuando se pulsa F7 el programa se ejecuta desde la ultima senten-
cia ejecutada hasta la linea donde se encuentra el cursor.
Si se desea procesar el programa paso a paso de una forma automa-
tica, ejecutar la orden Animate del menu presentado por la opci6n
Run. Una vez iniciada la animaci6n de un programa puede ser de-
tenida pulsando cualquier tecla. La velocidad de ejecuci6n automa-
tica se puede aumentar 0 disminuir mediante la orden Trace Speed
del menu Options.
Ejecutar la orden Restart cada vez que iniciemos de nuevo el proceso
de depuraci6n.
Para salir del depurador ejecutar la orden Exit del menu presentado
por la opci6n File.
Cuando se entra en Code View, mediante la orden CV, 10 primero que apa-
rece es el menu principal y tres ventanas: la ventana local para mostrar las
variables locales, la ventana source para mostrar el texto del programa a
depurar y la ventana command para emitir 6rdenes para el depurador. El
menu principal consta de ocho opciones: File, Edit, View, Search, Run,
Watch, Options, y Calls. Para seleccionar una de las opciones presentadas,
se puede optar por cualquiera de las dos formas siguientes:
• Pulsar la tecla AIt, para activar el menu principal, y despues la te-
cla correspondiente a la letra que se presenta en alto brillo 0 color
diferente (es indiferente el utilizar mayusculas 0 minusculas). Por
ejemplo W para activar el menu correspondiente a la opci6n Watch.
• Pulsar la tecla Ait para activar el menu principal y utilizando las
teclas de movimiento del cursor elegir la opci6n deseada (opci6n que
se presentani en video invertido respecto al existente 0 en color di-
ferente) y pulsar la tecla Enter.
Para seleccionar una orden correspondiente al menu presentado por
una opci6n del menu principal se puede pro ceder de cualquiera de las dos
formas siguientes:
• Pulsar la tecla correspondiente a la letra que se presenta en alto bri-
110 0 color diferente (es indiferente el utilizar mayusculas 0 minus-
culas). Por ejemplo si se esta en el menu presentado por la opci6n
File y se quiere salir de Code View, se pulsa la tecla x.
• Moverse a la orden deseada por medio de las teclas de movimiento
del cursor y pulsar Enter.
Algunas 6rdenes de estos menus tienen escrito a su derecha el nombre
de una tecla 0 combinaci6n de teclas que realizan la misma operaci6n. Par
ejemplo la primera orden del menu de la opci6n Watch es "Add Watch...
Ctrl +W". Esto significa que al pulsar las teclas Ctrl +W se ejecuta la or·
den Add Watch.
Algunas 6rdenes abren una ventana de dialogo; por ejemplo la orden
"Find ..." del menu presentado por la opci6n Search. En este caso respon-
deremos alas cuestiones planteadas y finalizaremos pulsando a continua-
ci6n Enter « OK ».
Para obtener ayuda sobre cualquier orden, seleccionarla y pulsar Fl.
Para abandonar la pantalla de ayuda pulsar la tecla Ese.
Code View esta disefiado para utilizar un raton de Microsoft 0 uno com-
patible con este.
1. Se apunta a la opcion deseada del menu y se puisa el boton iz-
quierdo del raton.
Para enrollar/desenrollar el texto sobre la ventana activa se dispone
de una barra de scroll vertical y otra de scroll horizontal. Ambas tienen
en sus extremos unas flechas que indican el desplazamiento imaginario de
la pantalla sobre el texto cuando se efectua la accion de scroll y un cursor
que se desplaza a 10 largo de la linea de scroll, que indica la posicion relati-
va del cursor de pantalla con respecto a los limites del texto.
1. Para avanzar 0 retroceder una linea se apunta a la flecha superior
o inferior de la barra de scroll vertical y se pulsa el boton izquier-
do del raton. Para desplazar el texto una posicion a la izquierda
o a la derecha se apunta a la flecha derecha 0 izquierda de la ba-
rra de scroll horizontal y se pulsa el boton izquierdo del raton.
2. Para avanzar 0 retroceder una pagina, se coloca el cursor del ra-
ton sobre la linea de scroll vertical, entre el cursor de scroll y la
flecha inferior 0 entre el cursor de scroll y la flecha superior y se
pulsa el boton izquierdo del raton. La operacion es analoga para
desplazar el texto una pagina hacia la izquierda 0 hacia la dere-
cha; eso S1, actuando sobre la barra de scroll horizontal.
3. Si se apunta al cursor de scroll vertical de la ventana de texto y
se tira, con el boton izquierdo del raton puisado, hacia arriba 0
hacia abajo arrastrandolo sobre la linea de scroll, el texto se des-
plaza hacia abajo 0 hacia arriba. La accion se ve cuando se deja
de pulsar e! boton. Esta misma operacion se puede realizar sabre
la linea de scroll horizontal para desplazar el texto hacia la izquierda
o hacia la derecha.
Es posible variar el tamafio de una ventana. Para ello, se apunta a la
linea de separacion entre ventanas y con el boton izquierdo del raton pul-
sado se tira en la direccion apropiada para agrandar 0 reducir la ventana.
Para activar una ventana, apuntar a cualquier lugar dentro de la mis-
ma y pulsar el boton izquierdo del raton.
Para activar 0 desactivar cualquier accion dentro de una ventana de
dhilogo, apuntar al espacio entre corchetes 0 entre angulos y pulsar el ba-
ton izquierdo del raton.
Muchas de las ordenes que se exponen a continuacion requieren la selec-
cion previa de un conjunto de caracteres, palabras 0 lineas. Para realizar
esta operacion dentro de la ventana activa, colocar el cursor al principio
del texto a seleccionar y manteniendo pulsada la tecla Shift, marcar eltex·
to desplazando e1cursor con las teclas de movimiento.
Tambien es posible seleccionar texto con el raton. Para ello, se apunta
al principio del texto a seleccionar y manteniendo pulsado el boton izquierdo
se tira en la direccion deseada hasta marcar el texto.
Cuando se ejecuta esta orden se nos pregunta por el nombre del nuevofi-
chero que deseamos cargar en la ventana source. Este fichero puede serun
fichero fuente, un fichero .h 0 cualquier otro fichero de texto. Cuandose
acabe de ver e1fichero cargado se puede volver a cargar el fichero original.
Permite se1eccionar uno de 10sm6du10s fuente que componen e1programa
y cargarlo en 1a ventana source.
Permite escribir todo 0 parte del fichero visualizado en 1aventana activa
a un fichero 0 dispositivo especificado (por ejemp10 LPTl).
Esta orden permite salir tempora1mente a1DOS pudiendo as! ejecutar cual-
quier otra tarea bajo e1sistema operativo. La vuelta a Code View se hace
ejecutando 1a orden Exit.
Deshace 10s cambios efectuados en 1a linea que estamos editando. No es
posib1e vo1ver a una linea ya abandonada y restaurarla.
Copia e1texto se1eccionado de 1aventana activa y 10salva en una memoria
intermedia, permaneciendo en 1a misma hasta que copiemos otro texto.
Copia en la ventana activa el texto almacenado en la memoria intermedia.
Podemos realizar las dos operaciones siguientes:
Reemplazar texto: seleccionar el texto que deseamos reemplazar en la
ventana activa y ejecutar Paste.
Insertar texto: mover el cursor a la posici6n deseada dentro de la ven-
tana activa y ejecutar Paste. El texto se inserta a continuaci6n del cursor.
• c6digo fuente solamente
• instrucciones en lenguaje ensamblador
• c6digo fuente mezclado con las correspondientes instrucciones en
lenguaje ensamblador.
Abre una nueva ventana source. Una ventana fuente sirve para mostrar el
c6digo fuente de un programa. El c6digo puede aparecer en uno de los tres
modos siguientes:
Abre una nueva ventana memory y visualiza en ella una zona contigua de
memoria. Para especificar la direcci6n de comienzo, ejecutar la orden "Me-
mory Window" del menu Options.
Activa/desactiva la ventana register. Esta ventana visualiza el estado ac-
tual de los registros del microprocesador 80x86.
Activa/desactiva la ventana 8087. Esta ventana visualiza el estado actual
de los registros del coprocesador matematico 80x87.
Activa/desactiva la ventana local. Esta ventana visualiza todas las varia-
bles locales de la funcion que actualmente se este ejecutando.
En esta ventana no se pueden afiadir ni borrar variables. Si se permite
modificar el valor de una variable.
Activa/desactiva la ventana watch. Esta venta:na muestra los valores de las
variables y expresiones seleccionadas.
Para afiadir una variable 0 una expresion a la ventana watch, ejecutar
la orden "Add Watch" del menu Watch.
Es posible modificar el valor de una variable 0 de una expresion incluida
en cualquiera de estas ventanas. Esto se hace escribiendo encima del valor
actual. Para restaurar el valor original pulsar la tecla Esc.
Cualquier elemento de datos (estructura, array 0 puntero) puede ser
expandido 0 contraido si la ventana, local 0 watch, esta activa. Un elemen-
to de datos que esta precedido por el signo mas (+) significa que puede
ser expandido y si esta precedido por el signo menos (-) significa que pue-
de ser contraido.
Para expandir 0 contraer un elemento de datos, apuntar al mismo can
el raton y pulsar dos veces el boton izquierdo; 0 bien, mover el cursor al
elemento de datos y pulsar Enter.
Activa/desactiva la ventana command. Esta ventana permite emitir orde-
nes para Code View. Estas ordenes nos permiten hacer de una forma direc-
ta las operaciones que realizamos a traves de los menus.
Activa/desactiva la ventana help. Esta ventana visualiza informacion de ayu-
da a cerca de Code View y palabras clave del lenguaje.
• ejecutar help
• pulsar Shift + Fl
• situar el cursor sobre una palabra clave dellenguaje, sobre una Of-
den de un menu 0 sobre una accion en una ventana de dialogo y
pulsar Fl.
Esta orden hace que se visualice la pantalla de los resultados. Para volver
a la ventana de texto pulsar F4.
Esta orden permite agrandar la ventana activa si esta a tamafio normal 0
ponerla a tamafio normal si esta agrandada. Cuando la ventana esta agran-
dada aparece sobre el menu View la orden Restore en lugar de Maximize.
Para realizar esta operaci6n con el rat6n, apuntar a la cajita de la es-
quin.asuperior derecha de la ventana y pulsar el bot6n izquierdo del rat6n.
Permite modificar el tamafio de la ventana activa utilizando las teclas de
movimiento del cursor. Para finalizar esta operaci6n pulsar Enter; para aban-
donar esta operaci6~ sin modificaciones, pulsar Ese.
Esta orden cierra la ventana activa. Para realizar esta operaci6n con el ra-
t6n, apuntar a la cajita de la esquina superior izquierda de la ventana y
pulsar el bot6n izquierdo del rat6n.
Utilizar esta orden para buscar en la ventana activa la siguiente ocurrencia
del texto especificado. Podemos requerir buscar palabras enteras (Whole
Word), hacer distinci6n 0 no entre letras mayusculas y minusculas (Case
Insensitive) y utilizar expresiones regulares (Regular Expression).
Cuando se requiere utilizar expresiones regulares pueden utilizarse como
como dines los siguientes caracteres:
la localizaci6n del texto escrito despues de este canicter debe darse
al principio de una linea.
la localizaci6n del texto escrito antes de este canicter debe darse
al final de una linea.
[ ] localizar uno de los caracteres del conjunto especificado entre cor-
chetes. Dentro de los corchetes pueden utilizarse los caracteres es-
peciales:
para indicar los limites del conjunto de caracteres que seae·
sea especificar.
 cualquier canicter de los indicados precedido por este caracter pierde
su significado especial y es considerado como un caracter normal.
Permite buscar un texto seleccionado previamente. El texto seleccionado
debe estar sobre una unica linea.
Esta orden permite repetir la ultima busqueda realizada por Find 0 por
Select Text.
Esta orden se utiliza para buscar una etiqueta 0 un nombre de una funci6n
en un programa en lenguaje ensamblador.
Esta orden hace que la siguiente sentencia a ejecutar sea de nuevo la pri-
mera sentencia de la funci6n main( ). lnicializa todas las variables que in-
tervienen en el programa. Ejecutar esta orden antes de una nueva ejecuci6n.
Ejecuta el programa paso a paso automaticamente desde la instrucci6n ac-
tual. Si el programa ya ha side ejecutado anteriormente hay que ejecutar
previamente la orden Restart. Una vez iniciada la animaci6n de un progra-
ma puede ser detenida pulsando cualquier tecla. La velocidad de ejecuci6n
automatica se puede aumentar 0 disminuir mediante la orden Trace Speed
del menu Options.
Esta orden permite especificar los argumentos en linea de 6rdenes necesa-
rias para la ejecuci6n del programa.
Esta orden permite registrar la historia del proceso de depuraci6n 0 utiIi-
zar la historia ya registrada. Durante esta operaci6n se crean dos ficheros
con un nombre base (prog) igual al nombre del programa. Estos ficherosson:
Para registrar la historia del proceso de depuraci6n, activar HistoQ
On y ejecutar el programa paso a paso.
Para retroceder y avanzar sobre la historia creada hasta el momenta
actual, utilizar las teclas Shift +F8 Y Shift +FIO respectivamente.
Esta orden repite toda la historia de un proceso de depuraci6n si la orden
History On esta activa.
Para repetir parte de la historia, desde el principio hasta un determi-
nado punto, primero situarse en el punta de la historia hasta el cual sequiere
repetir el proceso de depuraci6n y a continuaci6n ejecutar la orden Replay.
Esta orden permite afiadir en la ventana watch variables y expresiones cuyo
valor se quiere visualizar. Los valores senin visualizados cuando se ejecute
el programa paso a paso.
Una expresi6n puede ser una variable de cualquier tipo 0 una expre-
si6n C valida. Una expresi6n de relaci6n dara un resultado falso (0) 0 ver-
dadero(l).
Para visualizar una expresi6n de estas con formato de salida diferente
al indicado en el programa, escribir una coma seguida por un canicter de
formato.
resto
resto, f
resto, x
(formato: el indicado en el programa)
(formato: real)
(formato: hexadecimal)
Esta orden permite borrar una expresi6n 0 todas las expresiones de la ven-
tana Watch. Para borrar una expresi6n, seleccionarla y pulsar Enter. Para
borrar todas la expresiones elegir la opci6n Clear All.
Esta orden permite poner un punta de parada. Un punta de parada es una
pausa que se hace en un lugar determinado del programa. Esto nos permi-
te verificar los valores que tienen las variables en ese instante. Colocar los
puntos de parada donde se sospeche que puede darse un error.
Para poner un punta de parada, colocar el cursor en el lugar donde
debe tener lugar la acci6n y pulsar F9 0 ejecutar la orden Set Breakpoint.
Para quitar un punto de parada, situar el cursor en ellugar donde esta puesto
y pulsar F9.
Define el formato con el que se visualizara una zona de memoria en la ven-
tana memory.
Permite seleccionar la velocidad de ejecuci6n paso a paso para la orden
Animate.
Esta orden permite seleccionar e1tipo de expresiones que se van a evaluar,
esto es, expresiones Basic, C 0 Fortran. Si activamos la opci6n Auto, el de-
purador reconoceni automaticamente en funci6n del programa cargado el
tipo de expresi6n que tiene que evaluar.
Activa/desactiva e1intercambio de paginas de video entre Code View y el
programa que se esta depurando (ver las opciones IF y IS). La opci6n swap
desactivada requiere menos memoria y hace que el proceso sea mas rapido
pero tiene una serie de limitaciones que no se dan cuando esta activa.
Indica a Code View si en la repetici6n de la historia del proceso de depura-
ci6n se utiliza el fichero .cvh, el fichero .cvi 0 ambos (ver la orden History
On descrita anteriormente).
Esta orden solamente puede ser ejecutada si se tiene un procesador 386.
Cuando la opci6n 386 esta activada la ventana register visualiza los regis-
tros en el formato 80386. Tambien permite ejecutar instrucciones que refe-
rencian registros de 32 bits. Si no esta activa cualquier dato almacenado
en la parte alta de un registro de 32 bits se perdera. Esta opci6n no esta
disponible en modo protegido (CVP).
Esta orden presenta una lista de nombres de funciones, ademas del modu-
lo principal, que han sido llamadas para su ejecucion y que aun no han
finalizado. Cada llamada es mostrada con los argumentos que se han pa-
sado a la funcion.
A esta lista de llamadas en ocasiones se Ie da el nombre de pila porque
el nombre que esta en la parte superior es la ultima llamada. Cada vezque
una funcion termina de ejecutarse su nombre desaparece de la pila.
Por ejemplo si el modulo principal Ml llama a una funcion Sl, esta
a una funcion S2 y esta a una funcion S3, cuando la funcion S3 este en
ejecucion, en la pila habra, de arriba a abajo: S3, S2, Sl, Ml. Cuando fi-
nalice la ejecucion de S3, en la pila quedara S2, Sl, Ml y as! sucesivamente.
Supongamos que detenemos la ejecucion cuando se esta ejecutando
la funcion S3 y visualizamos el menu Calls. Si mediante las teclas de movi-
miento del cursor nos situamos sobre el nombre Sl y pulsamos Enter, Code
View coloca el cursor sobre la siguiente sentencia que seria ejecutada cuando
el control sea devuelto a la funcion Sl y muestra en la ventana local las
variables correspondientes a esta funcion. Si en este instante pulsamos la
tecla F7la ejecucion continuara hasta esta sentencia (sentencia sobre la que
esta el cursor).
Comprime un fichero ejecutable reduciendo el espacio ocupado por la in-
formacion afiadida al fichero para hacer posible su depuracion. EI fichero
comprimido puede ser cargado por Code View mas rapidamente.
Esta utili dad permite crear y modificar una base de datos de ayuda a par-
tir de uno 0 mas ficheros que contienen informacion formateada para el
sistema de ayuda.
Convierte programas en modo protegido para que puedan correr tanto en
modo protegido como en modo real. Una vez convertido, el mismo pro-
grama puede correr bajo DOS y OS/2.
Los programas en modo protegido utilizan librerias enlazadas dina-
micamente (DLL) para cargar en tiempo de ejecuci6n las funciones desde
las librerias almacenadas en ficheros. Los programas en modo real inclu-
yen las funciones de libreria en el fichero ejecutable.
Visualiza y modifica el contenido de la cabecera de un fichero ejecutable.
EXEHDR genera un listado que muestra el contenido de la cabecera del
fichero e informaci6n a cerca de cada segmento en el fichero.
Borra todos los ficheros del subdirectorio oculto DELETED del directorio
actual 0 deldirectorio especificado.
Mueve el fichero 0 ficheros especificados al subdirectorio oculto DELE-
TED. Para recuperar estos ficheros utilizaremos UNDEL.
Mueve el fichero 0 ficheros especificados del subdirectorio oculto DELE-
TED, al directorio padre.
La potencia de Microsoft C aumenta con la facilidad que tiene para incor-
porar 0 Hamar a rutinas en lenguaje ensamblador. En este capitulo se ex-
plica c6mo insertar en un m6dulo C rutinas escritas en lenguaje ensambla-
dor (rutinas en linea en ensamblador) y c6mo escribir un m6dulo separado
en lenguaje ensamblador para despues enlazarlo con otros m6dulos C.
Toda persona conocedora dellenguaje ensamblador es consciente de
la eficiencia y velocidad de las rutinas desarroHadas en dicho lenguaje. Tam-
bien sabe que el programar rutinas largas en lenguaje ensamblador puede
ser tedioso. Por esta raz6n, ellenguaje ensamblador es a menudo reserva-
do para pequefias tareas 0 rutinas espedficas dentro de un programa en
lenguaje de alto nive!.
• Hamar alas rutinas del DOS y del BIOS con la instrucci6n INT
• crear rutinas residentes (TSR)
Las rutinas en lenguaje ensamblador incorporadas en un programaC
pueden hacer que el programa no sea portable, por 10 que se aconseja que
estas rutin as se escriban como funciones 0 como m6dulos separadas para
que, en caso necesario, puedan ser reescritas con facilidad.
RUTINAS EN LENGUAJE ENSAMBLADOR EN LINEA CON
SENTENCIAS C
Una rutina en ensamblador, escrita en linea con las sentencias C que for-
man el programa, va precedida por la palabra clave_asm. La palabra_asm
llama al ensamblador; puede aparecer en cualquier lugar valida para una
sentencia C e ira seguida por una instrucci6n en ensamblador 0 par un grupo
de instrucciones en ensamblador encerradas entre llaves.
main( )
[
void pagina(short),o
pagina(O),o
}
void pagina(short pag)
[
mov ah,5
mov ai, byte ptr pag
int lOh,o /lamar al BIOS
Este ejemplo muestra una funci6n C formada por una rutina en en-
samblador. Las instrucciones en ensamblador van encerradas entre Haves
y encabezadas por la palabra reservada _asm.
Una rutina en ensamblador puede tambien especificarse no como una
funcion, sino como un bloque incluido en el propio modulo C.
# include <stdio.h >
main( )
[
system (Hcls"),o
printj(HPulse una tecla para continuar "),o
_asm
[
push
mov
int
pop
l
ax
ah, 0
16h
ax
; salvar ax
; funci6n: leer tecla
; llamar al BIOS
; restaurar ax
printj(H  nFin del proceso"),o
l
Una rutina en ensamblador puede escribirse tambien de las siguientes
formas:
1. Lineas independientes, precedidas cada una de ellas por la pala;
bra clave _asm. Por ejemplo:
# include < stdio.h >
# include <stdlib.h>
main( )
[
system (Hcls"),o
printj(HPulse una tecla para continuar "),o
_asmpush
_asm mov
_asm int
_asm pop
ax
ah, 0
16h
ax
; salvar ax
; funci6n: leer tecla
; llamar al BIOS
; restaurar ax
printj(H  nFin del proceso");
}
2. Una instrucci6n a continuaci6n de otra, actuando como separa-
dor la palabra clave _asm. Por ejemplo:
• Sin Havesel compilador no puede distinguir donde finaliza elcadi·
go ensamblador y donde comienza el c6digo C.
# include <stdio.h >
# include <stdlib.h>
main( )
[
system t'cls' ');
printf(HPulse una tecla para continuizr ");
printj(H  nFin del proceso");
}
En cualquier caso se recomienda incluir el c6digo en lenguaje ensam-
blador entre Haves, por las siguientes razones:
• Las Havesseparan de una forma clara el c6digo en ensamblador del
c6digo C.
Las Havesutilizadas en un bloque _asm no afectan al ambito deutili·
zaci6n de las variables como sucede en C.
En un programa Microsoft C, un bloque _asm soporta el conjunto com·
pleto de instrucciones correspondientes a los procesadores 80286y 80287
pero no reconoce las instrucciones que sean especificas de los procesado-
res 80386 y 80387. Para utilizar instrucciones especificas de los procesado-
res 80286 y 80287 el programa C debe ser compilado con la opci6n /G2.
Una constante entera puede ser expresada en las bases 2, 8, 10 y 16 afia-
dieildo el sufijo: B, Q 0 0, D y H respectivamente. Tambien puede emplearse
la notacion C; por ejemplo las constantes lOOh y OxlOO tienen el mismo
significado.
Aunque un bloque _asm puede referenciar objetos y tipos de datos C, no
puede definir objetos para contener datos utilizando las directrices y ope-
radores del MASM. Especificamente, no se pueden utilizar las directrices:
DB, DW, DD, DQ, DT y DF 0 los operadores DUP 0 THIS. Tampoco es-
tan disponibles las estructuras y registros del MASM; por 10 que no se pueden
utilizar las directrices: STRUC 0 RECORD. Sf son soportadas las directri-
ces EVEN y ALING.
Un bloque _asm en Microsoft C reconoce todas las expresiones soporta-
das por el Macro Ensamblador de Microsoft MASM, esto es, cualquier com-
binacion de operandos y operadores que den como resultado un unico va-
lor 0 una direccion. Tambien reconoce todos los operadores con las siguientes
excepciones 0 diferencias:
Los segmentos no pueden ser nombrados. Utilizar explicitamente un regis-
tro; por ejemplo ES:[BX]. Si se utiliza el operador PTR, debe aparecer an-
tes de modificar el segmento.
Sise utiliza el mismo identificador para un miembro en mas de una estruc-
tura 0 union, el nombre de la estructura 0 union deben especificarse inme-
diatamente antes del operador " . ".
No es soportado en union con el operador DUP pero puede ser utilizado
para devolver e1numero de elementos de un array. Para una variable esca-
lar (no array) devuelve el valor 1.
No es soportado en union con el operador DUP pero puede ser utilizado
para devolver e1tamafio de una variable. El tamafio de una variable es el
producto de los valores devueltos por los operadores LENGHT y TYPE.
Este operador puede ser utilizado en un bloque _asm para devolver el ta-
mafio de un tipo 0 de una variable C. Si la variable es un array, TYPE de-
vuelve el tamafio de un elemento del array.
Los resultados al aplicar los operadores anteriores y las expresiones equi-
valentes en C son los siguientes:
El ensamblador utilizado para codificar rutinas en linea no es un macro
ensamblador (MASM). Par ella, las macro directrices MACRO, REPT, IRC,
IRP y ENDM y los macro operadores < >, !, &, 0,10, WIDTH, MASK y
.TYPE no son soportados. En cambio, en un bloque _asm, si es posible
incluir directrices del preprocesador de C.
Las instrucciones en un bloque _asm pueden utilizar comentarios estilo
ensamblador. Par ejemplo:
Evitar utilizar este tipo de comentarios en una macro C; ya que C ex-
pande la macro en una unica linea 16gica.
La pseudoinstrucci6n _emit es similar ala directriz DB de MASM. Per-
mite definir un byte en la posici6n actual del segmento de c6digo actual.
Este byte se convierte en el byte apuntado por el registro IP (puntero de
instrucciones); 10 que equivale a decir que este es el primer byte de la si-
guiente instrucci6n a ejecutar. Esto es, _emit provee un mecanismo para
introducir instrucciones maquina byte a byte. Una aplicaci6n can creta de
_emit es definir instrucciones especificas del procesador 80386 que no es-
tan soportadas por un bloque _asm.
El siguiente ejemplo define la instrucci6n CWDE del 80386, la cual
convierte un valor can signo de 16 bits en AX, a un valor can signa de
32 bits en EAX.
1* Se asume el modo 16 bits */
#define cwde _asm _emit Ox66_asm _emit Ox98
El c6digo maquina equivalente a CWDE es 98 66 (hexadecimal); estos
bytes son introducidos por la macro cwde (primero el de menor peso) en
el segmento de c6digo como si de la propia instrucci6n se tratara. Por 10
tanto el procesador interpretara la macro cwde como la instrucci6n CWDE
del 80386.
Como hemos visto, un bloque _asm puede insertarse en un bloque C. Lo-
gicamente, las instrucciones del bloque _asm, igual que las sentencias de
C, pueden referirse a las variables C y utilizar otros elementos de C. Tales
elementos son los siguientes:
• Constantes, incluyendo constantes simb6licas y miembros de una
enumeraci6n (enum)
• Nombres de tipos declarados por typedeJ, generalmente utilizados
con operadores tales como PTR y TYPE 0 para especificar los miem-
bros de una estructura 0 uni6n.
Entendiendo por simbolo C un nombre de v<;lriable,etiqueta 0 un nom-
bre de una funci6n (no constantes simb61icas 0 miembros enum) las siguien-
tes reglas son aplicables':
1. Solamente un simbolo C puede ser referenciado par una ins truc-
ci6n en lenguaje ensamblador. Dos 0 mas simbolos no pueden apa-
recer en la misma instrucci6n en ensamblador a menos que se uti-
licen con los operadores TYPE, LENGTH 0 SIZE.
2. Las funciones referenciadas en un bloque _asm, deben ser decla-
radas anteriormente (funci6n prototipo), con el fin de que en un
bloque _asm, Microsoft C pueda diferenciar un nombre de fun-
ci6n de una etiqueta.
3. Un bloque _asm no puede utilizar simbolos C que coincidan con
palabras reservadas MASM. Por ejemplo, POP, PUSH y SI son
palabras reservadas para MASM.
4. Un bloque _asm puede referenciar todas las variables y demas
simbolos que esten dentro del ambito del bloque donde aparece
_asm.
# include <:stdio.h >
# include <std!ib.h>
main( )
(
void cursor-----xy(unsigned char y, unsigned char x);
unsigned char fila, co!;
system ("cls ");
-Fila= 23· co! = 5·J' , ,
cursor-----xy(fila,co!);
printj("Pu!se una tecla para continuar");
l
/ * Posicionar el cursor en la pantalla (suponemos pagina 0).
* Si fila 0 columna estan fuera de limites, se ignora.
*/
void cursor-----xy(unsigned char y, unsigned char x)
[
_asm
[
; Aceptar valores x, y
mov
cmp
jl
cmp
jg
dl, x
dl, 0
fin
dl, 79
fin
; dl = columna
; si col < 0, ignorar
mov dh, y
cmp dh,O
jl fin
cmp dh,24
jg fin
; Posicionar el cursor
mov
mov
int
fin: nop
}
}
; dh = fila
; si fila < 0, ignorar
bh, 0
ah,2
10h
; pagina 0
; funcion posicionar cursor
; llamar al BIOS
Los operadores identificados de la misma forma en C que en MASM
(*, I],...)tienen el significado propio del contexto en el que se utilicen.
int a[20],o
_asm mov a[5], ax
a[5] = b,o
; almacena ax en la posicion a+5
/ * almacena b en la posicion a+ 10 */
Una macro en C debe seI:escrita sobre una unica linea 16gica.Por ello, cuan-
do la macro ocupe mas de una linea fisica, utilizaremos como canicter de
continuaci6n la barra invertida (  ). Para escribir una macro y evitar re-
sultados inesperados, es aconsejable seguir las siguientes reglas:
• Comenzar cada instrucci6n en ensamblador con la palabra clave
_asm.
• Introducir los comentarios utilizando la notaci6n / * */ para que
quede perfectamente delimitado.
# define pagina(pag) _asm 
/ * Establecer la pdgina de visualizaci6n activa */ 
[ 
_asm mov
_asm mov
_asm int
ah,5 
ai, byte ptr pag 
10h 
main( )
[
int a[20];
pagina(1); a[5] = 10;
printf(H%d n': a[5J);
}
En este ejemplo las Havespara delimitar el bloque _asm que forma
la macro son esenciales. De otro modo el compilador asume las instruccio-
nes sobre la misma linea, ala derecha de la macro, como c6digo ensambla-
dor adicional.
En general, cuando un bloque _asm comienza a ejecutarse no asumimos
que un registro tenga un determinado valor; es decir, actuamos de igual
forma que con cualquier otra sentencia C.
Cuando escribimos un bloque _asm en linea con sentencias C, las
instrucciones que componen dicho bloque son libres de alterar los regis-
tros AX, BX, CX, y DX. Esto es aplicable tambien a los registros DI y Sl
con algunas excepciones (por ejemplo, C utiliza estos registros cuando de-
finimos variables tipo register).Sin embargo, si hay que salvar los registros
SP y BP, a no ser que tengamos alguna raz6n para cambiarlos de valor.
Cuando escribimos un bloque _asm como una funci6n no es necesa-
rio salvar los registros AX, BX, CX, DX, ES y flags. Sin embargo, si hay
que salvar cualquier otro registro (DI, SI, DS, SS, SP y BP), a no ser que
tengamos alguna raz6n para cambiarlos de valor.
Las funciones utilizan los registros AX y DX para devolver los resul-
tados. Si el valor devuelto es de tipo short, C asume el registro AX y si
el valor es de tipo long, C asume los registros DX y AX, colocando en este
caso la palabra de mayor peso, del resultado, en el registro DX y la palabra
de menor peso, en el registro AX. Para devolver un valor real la funci6n
debe colocar el valor en memoria y devolver un puntero a dicho valor (en
AX si es near y en DX:AX si es far).
Para funciones con bloques _asm, no utilizar eI convenio -Jastcall.
Cuando utilizamos el convenio -Jastcall de Hamada a funciones (opci6n
/Gr), e1compilador C almacena los argumentos pasados en registros en
lugar de en la pila. Esto puede traer problemas ya que no sabemos que re-
gistros se han utilizado. Por ejemplo, si la funci6n recibe un panimetro en
BX e inmediatamente nosotros almacenamos un valor en BX, el panime-
tro se ha perdido.
Calcular la raiz cuadrada entera de un numero entero, mediante el si-
guiente algoritmo: "la raiz cuadrada entera de un numero entero n es igual
al numero de enteros impares (1, 3, ...) que pueden restarse sucesivamente
de dicho numero".
Comprobar que el numero de enteros impares buscado se correspon-
de con el primer numero impar ni no restado, dividido por 2 (division entera).
Para ajustar la raiz al entero mas proximo, procederemos de la siguiente
forma:
raiz = nU2
si raiz*(raiz+])+] < = n entonces raiz
12 - 1
11 - 3
8 - 5
3 - 7
13 - 1
12 - 3
9 - 5
4 - 7
ni = 7, raiz = 7/2
3*4+1 > 12, raiz
ni = 7, raiz = 7/2 = 3
3*4+1 < = 13, raiz = 4
main( )
(
short sqrte(short n); / *prototipo */
short numero;
printft';, mimero ? "); scanft'%d': &numero);
printf(C<%d': sqrte(numero));
}
/ * Funci6n para ea/eu/ar /a ra[z euadrada de un nro. entero.
*/
short sqrte(short n)
{
_asm
{
; Entrada: ex
; Salida: ax
mov
mov
mov
jmp
ex, n
bx, ex
ax, 1
SHORT eero
; eargar e/ argumento
; haeer una eopia
; primer numero impar
;;.n<=O?
/azol: sub ex, ax ; ex = ex - ax
add ax, 2 ;ax=ax+2
eero: emp ex, ax ; si ex > = ax,
jge /azol ; saltar a /azol
sar
mov
ax, 1
ex, ax
; ax = nro. enteros imp.
; ra[z aproximada
imu/
add
ine
emp
jg
ine
fin:
J
ex
ax, ex
ax
ax, bx
fin
ex
;ax ex*ex
; ax ax + ex
;ax ax+l
; eomparar con n
; saltar si >
; si >, ex = ex + 1
Una funci6n C definida por el usuario 0 perteneciente alas librerias estan-
dar, puede ser llamada des de un bloque _asm utilizando la instrucci6n
call. En general, cuando se llama a una funci6n los parametros son pasa-
dos a la pila (de derecha a izquierda bajo el convenio _cdecl) para ser re-
cuperados de la misma cuando la fund6n se ejecuta. Esto nos indica la
forma de pro ceder para llamar a una fund6n desde un bloque _asm:
1. Salvar los parametros en la pila, empezando par el que esta mas
a la derecha (push).
char formato[ J = "%s = %d  n";
char *cadena = "Reg. AX";
main( )
{
int x = 10, y
z=x+y;
_asm
{
; visualizar el valor de ax
movax, Z
push ax
mov ax, cadena
push ax
mov ax, offset formato
push ax
call printj
mov cars, ax
; llamada a la funci6n
; valor retornado por printj
}
printj("caracteres escritos
}
El cuerpo de una funci6n C puede ser reemplazado por un bloque _asm.
Esto es uti! cuando en ocasiones necesitamos una mayor velocidad de eje-
cuci6n.
El siguiente ejemplo inicializa todos los elementos de un array a un
valor determinado.
/ * Inicializar un array */
# include <stdio.h>
main( )
(
int inicializar(int *v);
int vector[250], i;
/ *prototipo */
/ * declaraci6n del array */
inicializar(vector );
for (i = 0; i < 250; i+ +)
printjt'%d ': vector[i]);
/ * llamada a la funci6n */
/ * escribir el array */
/ * Esta funci6n inicializa el array apuntado por v */
int inicializar(int *v)
{
mov di, v
movax, ds
moves, ax
movax,96h
;-direcci6n de comienzo del array
; poner la direcci6n del segmento de
; datos, en "es': utilizado por stosw
; valor de inicializaci6n (150)
movex, Oxfa
cld
rep stosw
; numero de elementos (lei array (250)
; poner el pag de direeci6n a 0
; eseribir "ax" en eada elemento
Normalmente las interrupciones del DOS y del BIOS son procesadas utili-
zando las funciones int86( ) 0 int86x( ) como veremos en el capitulo si-
guiente. No obstante, como ya hemos visto en algunos ejemplos en este mis-
mo capitulo, es muy facil Hamar a una rutina de interrupci6n desde un
bloque _asm utilizando la instrucci6n into
El ejemplo que se muestra a continuaci6n indica de una forma sencilla c6mo
manipular punteros _based y punteros -far.
/ * Utilizaci6n de punteros en bloques _asm */
/ * Compi/ado bajo el modelo small */
# include <stdio.h>
# include <string.h >
# include <malloe.h >
char .-far *invertir(char _far *Str),o
-segment segmento1, seg_data,o
char _based(segmento1) *peadena,o
int main(void)
{
char eadena[81], _far *resu,o
if ((segmento1 = _bheapseg(2048)) = = -.NULLSEG)
{
puts("error 1: segmento no asignado"),o
exit(l),o
if ((pcadena = _bmalloc(segmento1, 81)) = = ---.NULLOFF)
(
puts(Herror 3: insujiciente espacio de memoria");
exit(3);
_asm {mov seg_data, ds }
printj(Hseg_data = %p  n': seg_data);
printf(Hsegmento 1 = %p  n': segmento1);
printf(Hpcadena = %fp  n': (char -far *)pcadena);
while (1)
{
printf(H n  nIntroducir cadena (0 FIN): ");
gets(cadena);
if (!stricmp(cadena, HFIN")) break;
-fstrcpy((char _far *)pcadena, (char _far *)cadena);
printf(H%Fs  n': (char _far *)pcadena);
resu = invertir(pcadena);
printj(H  n%Fs': resu);
}
/ * Liberar memoria */
_bjreeseg(segmento1);
_bjree(segmento1, pcadena);
}
char ~ar *invertir(char ~ar *pcad)
{
fes bx, pcad ; transjiere puntero de 32 bits
sub cx, cx ; 0 = cardcter de terminaci6n
meter: push cx ; meter caracteres en fa pi/a
mov c/, es:[bx] ; coger un cardcter de memoria
jcxz inver ,
inc bx ; siguiente cardcter
jmp meter ,
inver: fes bx, pcad ,
sacar: pop cx ; sacar caracteres de fa pi/a
mov
jcxz
inc
jmp
les
mov
es:[bx], cl
fin
bx
sacar
ax, pcad
dx, es
,. devolver desplazamiento
,. devolver segmento
Este ejemplo lee una cadena, la almacena en un segmento denomina-
do segmentol, la invierte, utilizando para ello la funci6n invertir( ) y escri-
be el resultado obtenido.
Observar en la funci6n main( ) c6mo se obtiene el valor del registro
DS utilizando un bloque _asm. La funci6n invertir( ) ha side disefia'da
para devolver un puntero far (segun el convenio C, en DX:AX). Observar
tambien que un puntero based ocupa dos bytes; cuando es pasado a la fun-
ci6n 0 cuando es devuelto por la funci6n, es tratado como un puntero far;
10 que indica que tanto el segmento como el desplazamiento de la direc-
ci6n de memoria son pasados y retornados.
Un miembro de una estructura 0 de una uni6n puede referenciarse dentro
de un bloque _asm, utilizando el nombre de la estructura y el nombre del
miembro (ademas del operador punto) 0 utilizando solamente el nombre
del miembro. Por ejemplo:
mov bx, offset estr
mov ax, [bx].miembro
Si el nombre del miembro aparece en mas de una estructura, es nece-
sario especificar el nombre de la estructura para evitar ambiguedades. Por
ejemplo:
mov bx, offset estr
mov ax, estr[bx}.miembro
El ejempl0 siguiente muestra como acceder desde un bloque _asm,
a 10s miembros de una estructura definida en C. Concretamente, este ejemplo
define dos estructuras: s1 y s2 asigna valores a la estructura s2 y copia s2
en s1.
main( )
{
static struct ts1
{
int m1, m2, sig;
};
static struct ts2
{
char car;
struct ts1 f;
struct ts2 *-Sig;
};
/ * Copiar fa estruetura s2 en fa estruetura sl */
_asm
{
sub ex, ex
mov cl, s2.ear
mov sl.ear, cl
mov ex, s2jm1
mov sljm1, ex
mov ex, s2jm2
mov sljm2, ex
mov ex, s2.j.sig
mov sl.j.sig, ex
mov bx, offset s2
mov ex, s2.sig
mov bx, offset sl
mov sI.sig, ex
printf(Hearcieter = %e  n': sI.ear);
printftj = %d-%d-%d n':
sl.j.ml, sl.j.m2, sl.j.sig);
printf(Hdireeci6n siguiente = %p  n': sI.sig);
J
Observar que el bloque _asm trabaja correctamente porque las es-
tructuras se han definido estaticas (almacenadas en el segmento de datos,
DS). Si hubieran sido declaradas automaticas (aulo), estarian almacena-
das en la pila; esto exigiria trabajar utilizando desplazamientos negativos
relativos al registro BP.
_asm
{
mov di, offset s2
mov si, offset sl
sub ex, ex
mov cl, [di].ear
mov [sij.ear, cl
mov ex, [dij.j.ml
mov [sij.j.ml, ex
mov ex, [dij.j.m2
mov [sij.j.m2, ex
mov ex, [dij.j.sig
mov [sij.j.sig, ex
mov ex, s2[dij.sig
mov sl[sij.sig, ex
J
Si en lugar de estructuras definimos punteros a estructuras, el bloquc
_asm no cambia.
Las instrucciones de bifurcaci6n en ensamblador y en C pueden utilizarse
para saltar a una etiqueta dentro de un bloque _asm 0 fuera de el. Cuan-
do se define una etiqueta en un bloque _asm no hay diferencia entre utili-
zar mayusculas 0 minusculas. En cambio, cuando se define una etiqueta
C sf existe esta diferencia.
main( )
{
goto et_c,·
goto et_c;
II correcto
II error: etiqueta no dejinida
goto et_asm;
goto et----.ASM;
_asm
(
imp et_C
imp et_c
II correcto
II correcto
II correcto
II error: etiqueta no dejinida
imp et_asm
imp et----.ASM
II correcto
II correcto
printf(HFIN  n ");
l
Un programa que contenga bloques _asm puede ser depurado con Code
View, sin ninguna restricci6n (ver el depurador Code View en el capitulo
anterior).
La presencia de un bloque _asm afecta a la optimizaci6n del progra-
ma en las formas siguientes:
• Como cabe esperar, un bloque _asm no es optimizado. El compi-
lador respeta el c6digo escrito en ensamblador.
• Bajo circunstancias normales el compilador automaticamente alma-
cena las variables en los registros. Cuando una funci6n contiene un
bloque _asm esto no ocurre a no ser que se indique explicitamente
con la palabra clave register.
Finalmente, cuando una funci6n contiene un bloque _asm, quedan
inhibidas en toda la funci6n las siguientes optimizaciones:
Para poder enlazar un procedimiento escrito en lenguaje ensamblador con
un programa C deben utilizarse segmentos compatibles. Los siguientes pun-
tos pueden ser de gran ayuda:
2. Utilizar las directrices .CODE y .DATA para declarar los segmen-
tos de c6digo y de datos respectivamente. (Con las versiones 5.0
o posteriores del macroensamblador de Microsoft utilizar el for-
mato simplificado).
3. EI nombre del procedimiento debe ser declarado con la directriz
PUBLIC. Tambien debe ser declarado PUBLIC cualquier otro data
que se quiera utilizar desde otro modulo.
4. Los procedimientos y los datos accedidos per la rutina en ensam-
blador deben ser declarados EXTERN. La forma mas segura de
utilizar EXTERN es colocar la directriz fuera de cualquier defini-
cion de segmento.
push bp ;salvar bp
mov bp, sp ;preparar para acceder a pardmetros
El registro BP es utilizado para acceder a los parametros y a los datos
locales almacenados en la pila (stack). EI registro SP no puede utilizarse
para este proposito porque no es un registro indice 0 base. Por otra parte,
SP cambia cuando se meten mas datos en la pila, mientras que el registro
BP permanece constante; asi que cada parametro puede ser direccionado
con un desplazamiento con respecto a BP.
EI valor de BP es salvado porque es necesario cuando termine la eje-
cucion del procedimiento para poder continuar con la ejecucion del
programa.
Un procedimiento llamado debe salvar los valores de los registros SI, DI,
SS Y DS ademas del BP que ya ha side salvado. Esto es:
push bp
mov bp, sp
push si
push di
push ss
push ds
Cuando salgamos del procedimiento, estos registros deben ser restau-
rados en el siguiente orden:
pop ds
pop ss
pop di
pop SI
pop bp
ret
1. La Hamada des de el program a pone en la pila cad a uno de los pa-
nimetros.
2. A continuaci6n se salva en la pila la direcci6n de retorno al pro-
grama. Esta direcci6n puede ser de 2 bytes (near) 0 de 4 bytes (far).
3. Despues el procedimiento salva el registro BP.
4. Por ultimo se salvan los registros SI, Dl etc.
panimetro
panimetro
panimetro
direcci6n de retorno
BP
SI
DI
Esto qui ere decir que en el caso de una Hamada (near) a un procedi-
miento que ha recibido un solo panimetro, dicho panimetro se encuentra
localizado en la direccion BP + 4.
Al salvar los datos en la pila siempre se pone la direccion del segmen-
to antes que la direccion offset (desplazamiento) y con los argumentos su-
peri ores a dos bytes, se pone siempre la palabra alta antes que la baja.
Dependiendo del tamafio en bytes del valor retornado, este es devuelto en
los registros:
1 byte
2 bytes
4 bytes
AL
AX
Parte alta (0 direccion del segmento) en DX
Parte baja (0 direccion offset) en AX
Si el valor retornado es mayor de 4 bytes, el procedimiento Hamada
por C debe asignar espacio para el valor a devolver y entonces colocar su
direccion en DX:AX. Una forma simple de asignar espacio para el valor
a devolver es declararlo en el segmento de datos.
Un programa C puede Hamar a un procedimiento en ensamblador almace-
nado en otro modulo, igual que Hama a una funcion. Los siguientes pasos
pueden servir de ayuda:
1. Declarar NEAR el procedimiento Hamado des de C, si el modulo
es compilado bajo uno de los modelos small 0 compact; declarar-
10 FAR, si este es compilado bajo uno de los modelos large, huge
o medium.
3. Los panimetros son colocados en la pila en orden inverso a como
aparecen en la llamada (de derecha a izquierda).
4. Por defecto los panimetros son pasados por valor, excepto los arrays
que son pasados por referencia.
5. Incluir un gui6n bajo delante de cualquier nombre que vaya a ser
com partido con C. Solamente los ocho primeros caracteres son
reconocidos.
6. Si al efectuar el enlace se utiliza la opci6n INOI, C no convierte
los nombres a mayusculas. Ensamblar con la opci6n IMX para
prevenir que MASM convierta los nombres a mayusculas.
Realizar el mismo programa anterior del calculo de la raiz cuadrada
de un numero entero, pero ahora utilizando un m6dulo separado en en-
samblador.
main( )
[
printjt~ mlmero ? "); scanft'%d': &numero);
printj("%d': sqrte(numero));
l
Este programa, llama para su ejecuci6n al procedimiento que se des-
cribe a continuaci6n, editado bajo el nombre de SQRTE.ASM, que es una
rutina para calcular la raiz cuadrada entera de un numero entero.
.MODEL SMALL
.CODE
---.Sqrte
,. Entrada: ex
,. Salida : ax
PUBLIC ---.Sqrte
PROC
push bp
mov bp,
mov ex,
mov
mov
jmp
[bp+4]
bx, ex
ax, 1
SHORT eero
,. salvar bp
,.preparar para aeeedera
,.pardmetros
,. eargar el argumento
,. haeer una eopia
,.primer mimero impar
, <: n <= 0 ?
lazol: sub ex, ax ,. ex = ex - ax
add ax, 2 ,. ax = ax + 2
eero: emp ex, ax ,. si ex > = ax,
jge lazol ,. saltar a lazol
ax, 1 ,. ax = nro. enteros imp.
mov ex, ax ,. razz aproximada
imul ex ,. ax = ex * ex
add ax, ex ,. ax = ax + ex
ine ax ,. ax = ax + 1
emp ax, bx ,. eomparar eon n
jg fin ,. saltar si >
ine ex ,. si >, ex = ex + 1
mov ax, ex
pop bp
ret
ENDP
END
Una vez editados los dos modulos anteriores, proceder de la forma
siguiente:
C: > MASM IMX SQRTE.ASM
C: >CL RAIZ.C SQRTE.OBJ
C:>RAIZ
C: > CL RAIZ.C IMAMX SQRTE.ASM
C:>RAIZ
se obtiene SQRTE.OBJ
se obtiene RAIZ.EXE
ejecutar RAIZ
se obtiene RAIZ.EXE
ejecutar RAIZ
COMUNICACIONES. SERVICIOS
DEL DOS Y DEL BIOS
El DOS dispone de numerosas rutinas que nosotros podemos utilizar acti-
vando la interrupci6n correspondiente. De todas ellas, la interrupci6n 21H
tiene un especial interes, ya que nos permite acceder a todos los servicios
del DOS. (Para sacar provecho a este capitulo debe conocerse la arquitec-
tura del microprocesador 8086 y las interrupciones y servicios de DOS).
C tambien dispone de funciones que nos van a permitir realizar la in-
terrupci6n requerida, y ejecutar asi la rutina deseada del DOS. Las funcio-
nes que se estudian en este capitulo utilizan las uniones y estructuras que
se presentan a continuaci6n y que estin declaradas en el fichero dos.h~
union REGS {
struct WORDREGS x;
struct BYTEREGS h;
Estructura: struct WORDREGS {
unsigned int ax;
unsigned int bx;
unsigned int ex;
unsigned'int dx;
unsigned int si;
unsigned int di;
unsigned int ejlag;
Estructura: struct BYTEREGS {
unsigned char ai, ah;
unsigned char bl, bh;
unsigned char c/, eh;
unsigned char dl, dh;
Estructura: struct SREGS (
unsigned int es;
unsigned int es;
unsigned int ss;
unsigned int ds;
Las declaraciones para las funciones que se detaIlan a continuacion estan
contenidas en el fichero dos.h.
Esta funcion es utilizada para realizar una interrupcion directamente al DOS
y ejecutar la rutina correspondiente.
La funcion int86( ) ejecuta la interrupcion especificada por n_int. An-
tes de ejecutarse la interrupcion, int86( ) copia el contenido de in_regs en
10sregistros correspondientes. Despues de ejecutarse la itlterrupcion, int86()
copia los valores actuales de los registros en out_regs. Tambien copia el
registro de flags en el campo cflag de out_regs.
No utilizar la funci6n int86( ) para interrupciones que modifiquen el
registro DS. En su lugar utilizar la funci6n int86x( ) 0 la funci6n intdosx( ).
EI valor devuelto por esta funci6n es el valor del registro AX despues
de la interrupci6n. Si el valor del campo cflag es distinto de 0, quiere decir
que ha ocurrido un error y la variable _doserrno es puesta al valor corres-
pondiente.
Esta funci6n es utilizada para realizar una interrupci6n directamente al DOS
y ejecutar una rutina que recibe un argumento en el registro ES, 0 que reci-
be en el registro DS un valor diferente del segmento de datos por defecto.
int int86x(int n_int, union REGS *in~regs, union REGS *out_regs,
struct SREGS *regs---.Seg);
La funci6n int86x( ) ejecuta la interrupci6n especificada por n_int.
Antes de ejecutarse la interrupci6n, int86x( ) copia el contenido de in_regs
y de regs---.Segen los registros correspondientes. De regs---.Seg,solamente
son utilizados los valores de los registros DS yES. Despues de ejecutarse
la interrupci6n, int86x( ) copia los valores actuales de los registros en
out_regs y los valores actuales de DS y ES en regs---.Seg,restaurando DS.
Tambien copia el registro de flags en el campo cflag de out_regs.
EI valor devuelto por esta funci6n es el valor del registro AX despues
de la interrupci6n. Si el valor del campo cflag es distinto de 0, quiere decir
que ha ocurrido un error y la variable _doserrno es puesta al valor corres-
pondiente.
Esta funci6n es utilizada para realizar una Hamada al DOS y ejecutar una
rutina que reciba argumentos en otros registros distintos de DX (DH/DL)
y AL, 0 para indicar errores en funci6n de los flags.
La fundon intdos( ) llama a la rutina del DOS especificada por los
valores de los registros en in-,"egs. Para efectuar la llamada, esta funci6n
ejecuta la instruccion INT 21H. Antes de ejecutarse la instrucdon, intdos( )
copia el contenido de in-,"egs en los registros correspondientes. Despues
de ejecutarse la instruccion INT 21H, la fundon copia los valores actuales
de los registros en out-,"egs. Tambien copia el registro de flags en el cam-
po cflag de out-,"egs.
No utilizar la funcion intdos( ) para interrupdones que modifiquen
el registro DS. En su lugar utilizar la fundon int86x( ) 0 la funcion int-
dosx( ).
El valor devuelto por esta funcion es el valor del registro AX despues
de la interrupcion. Si el valor del campo cflag es distinto de 0, qui ere decir
que ha ocurrido un error y la variable _doserrno es puesta al valor corres-
pondiente.
Esta funcion es utilizada para realizar una llamada al DOS y ejecutar una
rutina que reciba un argumento en el registro ES, 0 que redba en el regis-
tro DS un valor diferente del segmento de datos por defecto.
int intdosx(union REGS *in-,"egs, union REGS *out_regs,
struct SREGS *regs........seg);
La funcion intdosx( ) llama a la rutina del DOS espedficada por los
valores de los registros en in-,"egs. Para efectuar la llamada esta funci6n
ejecuta la instrucdon INT 21H. Antes de ejecutarse la instrucdon, la fun-
cion intdosx( ) copia el contenido de in-,"egs y de regs........segen los regis-
tros correspondientes. De regs........seg,solamente son utilizados los valores
de los registros DS yES. Despues de ejecutarse la instrucdon INT 21D,
la fundon copia los valores actuales de los registros en out-,"egs y los va-
lores actuales de DS yES en regs........seg,restaurando DS. Tambien copia
el registro de flags en el campo cflag de out_regs.
El valor devuelto por esta funci6n es el valor del registro AX despues
de la interrupci6n. Si el valor del campo c/lag es distinto de 0, quiere decir
que ha ocurrido un error y la variable _doserrno es puesta al valor corres-
pondiente.
Esta funci6n copia en una estructura de tipo SREGS apuntada por regs_seg
el contenido de los registros de segmentos.
Esta funci6n llama al DOS para ejecutar una rutina especificada par fo_dos
despues de copiar los contenidos de dos_dx y dos_a) en los registros DX
y AL respectivamente. Para efectuar la llamada esta funci6n ejecuta la ins-
trucci6n INT 21D.
La funci6n bdos() se utiliza para realizar llamadas al DOS yejecutar
rutinas que no tomen argumentos 0 que solamente los tomen en los regis-
tros DX (DH,DL) y/o AL.
No utilizar la funci6n bdos( ) para interrupciones que modifiquen el
registro DS. En su lugar utilizar la funci6n int86x( ) 0 la funci6n intdosx( ).
El valor devuelto por esta funci6n es el valor del registro AX despues
de la llamada.
Esta macro es utilizada para copiar 0 recuperar el offset de la direcci6n
far especificada por puotero.
Esta macro es utilizada para copiar 0 recuperar el segmento de la direcci6n
far especificada por puntero.
La macro FP _SEG devuelve como resultado el segmento de la direc-
cion far.
# include <dos.h >
# include <stdio.h>
# include <stdlib.h>
# include <conio.h >
# define DC1 Oxll
#define DC3 Ox13
#define INIT 0
#define SEND 1
#define READ 2
#define STAT 3
#define n 81
main( )
(
void rs232_inic( );
/* XON d
/* XOFF */
/ * Funcion 0 de fa interrupcion 14 */
/ * Funcion 1 de fa interrupcion 14 */
/ * Funcion 2 de fa interrupcion 14 d
/ * Funcion 3 de fa interrupcion 14 */
/ * Longitud maxima de fa cadena d
void rs232_lect( );
void rs232-----stat();
void rs232_envi( );
/ * Espera por un cardcter de COM */
/ * estado de COM */
/ * Enviar cardcter XON 0 XOFF */
FILE *pj;
char c, cadena[n};
int i = 0;
if ((pj = jopen("datos': "w")) = = NULL)
{
printjt'Error: El jichero datos no se puede abrir");
exit(I);
l
rs232_inic( );
systemt 'els' ');
printj("Recepci6n de datos. Pulse una teela para jinalizar");
while ( 1)
{
/ * Pulse una teela para jinalizar la recepci6n */
if (kbhit( ) != 0) exit(O);
rs232-----stat();
if ((v2.h.ah & 1) = = 1)
{
rs232_lect( );
cadena[i + +} = v2.h.al,·
if (i = = n)
{
vJ.h.al = DC3; / * enviar XOFF para detener */
rs232_envi( ); / * la transmisi6n */
jwrite(cadena, sizeof(cadena), 1, pj);
i = 0;
vJ.h.al = DC1;
rs232_envi( );
l
l
l
l
/ * leer el cardcter */
/ * almacenarlo */
/ * enviar XON para continuar */
/ * la transmisi6n */
void rs232_inic( )
{
v1.x.dx = 0;
v1.h.al = Oxfe;
v1.h.ah = INIT;
int86 (Ox14,&v1, &v2); /
J
/ * seleccionar puerto COM1 */
/ * seleccionar: Baudios, Paridad,
* bit Stop y Longitud palabra
* ( BBBPPSLL)
*/
/ *funci6n (0) de int 14 a realizar */
* llamada a la funci6n */
void rs232_lect( )
(
v1.h.ah = READ;
int86(20, &v1, &v2);
J
/ *funci6n (2) de int 14 a realizar */
/ * llamada a la funci6n */
void rs232----stat()
(
v1.h.ah = STAT;
int86(20, &v1, &v2);
J
hfunci6n (3) de int 14 a realizar */
/ * llamada a la funci6n */
void rs232_envi( )
(
v1.h.ah = SEND;
int86(20, &v1, &v2);
J
/ * funci6n (1) de int 14 a realizar */
/ * llamada a la funci6n */
# include <dos.h>
# include <stdio.h>
# include <stdlib.h>
union REGS in~egs, out~egs;
struct SREGS regs~eg;
main( )
(
char ~ar *buffer= "Finalizar cadenas con signo dolar  n  r  n  r$";
char ~ar *P;
systemt<c!s");
/ * Obtener la version de DOS utilizando la fun cion DOS Ox30. */
in~egs.h.ah = Ox30;
intdos( &in~egs, &out~egs );
printj("  nDOS version %d.%d  n  n' :out~egs.h.al,out~egs.h.ah);
/ * Escribir una cadena de caracteres finalizada con el signo $,
* utilizando la funcion DOS Ox9.
*/
in~egs.h.ah = Ox9;
in~egs.x.dx = FP_OFF( buffer );
regs~eg.ds = FP~EG( buffer );
intdosx( &in~egs, &out~egs, &regs~eg );
segread( &regs~eg );
printj( "Segmentos:
n  tCS t%.4x n tDS t%.4x n  tES t%.4x n tSS t%.4x n  n';
regs~eg.cs, regs~eg.ds, regs---..seg.es,regs~eg.ss );
/ * Asegurarse de que la impresora estd disponible.
* No estd disponible si cualquier bit de error estd en ON
* 0 si uno 0 ambos bits de operacion estdn en OFF.
*/
in~egs.h.ah = Ox2; / *funcion 2 (status) de la int Ox17 */
in~egs.x.dx = LPT1; / * impresora 0 */
int86( Ox17,&in~egs, &out~egs );
iff (out~egs.h.ah & Ox29) II !(out~egs.h.ah & Ox80) II
!(out~egs.h.ah & OxlO))
printj( "Impresora no disponible." );
else
{
/ * Escribir una cadena en la impresora utilizando la
*funci6n DOS Ox5.
*/
for( p = buffer; *P /= '$'; P+ + )
bdos( Ox05, *P, 0 );
/ * Escribir ef contenido de fa pantalla. */
in_regs.h.ah = Ox05;
int86( Ox05, &in_regs, &out_regs );
]
]
C dispone de un conjunto de funciones que permiten acceder alas rutinas
basicas de entrada-salida almacenadas en la ROM (ROM-BIOS).
La estructura utilizada para enviar y recibir informacion a y desde 105
servicios del disco es:
struct diskinfo_t (
unsigned drive;
unsigned head;
unsigned track;
unsigned sector;
unsigned nsectors;
void far *buffer;
La estructura diskinfo_t contiene los datos necesarios para ejecutar
un servicio. Hay seis servicios basicos:
Inicializa la unidad de disco y su controla-
dor. Para este servicio el panimetro info es
ignorado.
Informa del estado de la ultima operaci6n de
la unidad de disco. Para este servicio el pa-
nimetro info es ignorado.
Lee uno 0 mas sectores del disco, especifica-
dos en la estructura info, y los almacena en
la zona de memoria apuntada por buffer.
Escribe uno 0 mas sectores en el disco, espe-
cificados en la estructura info, con la infor-
maci6n almacenada en buffer.
Verifica uno 0 mas sectores del disco para ase-
gurar que los datos escritos son perfectamente
recuperables, 10 que significa comprobar la
paridad y otros defectos de grabaci6n. Tam-
bien hace un CRC (cyclic redundancy check).
No comprueba si los datos almacenados coin-
ciden con los de la memoria.
Se utiliza para formatear una pista del dis-
co, la especificada por los parametros head
y track. Este es el formate ffsico que sirve de
base al formate 16gico del DOS.
EI valor retornado depende del servicio. Los servicios reset y format
no retornan un valor significativo. Los servicios read, write y verify retor-
nan el numero de sectores procesados en el byte de menor peso. Si ocurre
un error, el servicio status coloca el c6digo correspondiente en el byte de
mayor peso. Un 0 en este byte significa que no se ha producido un error.
La configuraci6n del equipo se obtiene con la interrupci6n 17 (INT Oxll)
que esta obsoleta ya que entre otras cosas no da el tamafio de la memoria.
15,14
13
12
11,10,9
8
7,6
5,4
3,2
1
o
numero de puertos paralelo
1 si hay un modem instalado
1 si hay un adaptador de juegos
numero de puertos serie
o si hay un DMA (chip) instalado
unidades de disco flexible (0 a 4)
modo de video (00 no utilizado, 01 40x25 color,
10 80x25 color, 11 monocromo)
memoria RAM en bloques de 16K (11=64k)
1 si hay un coprocesador instalado
1 si hay unidades de disco
Esta funci6n devuelve una palabra de 16 bits indicando 10 que hay ins-
talado.
Lee el siguiente canicter desde el buffer de entrada del
teclado. Si no hay un canicter, espera por el.
Informa si hay algun canicter en el buffer de entrada del
teclado. EI canicter se queda en el buffer hasta que se
lea con el servicio O.
Comunica los bits de estado del teclado, que indican el
estado de las teclas de tipo shift (si se ha pulsado una
tecla Shift, Alt, Ctrl, etc.).
Esta funci6n retorna un valor que depende del servicio. Si el servicio
es 0 retorna en el byte de menor peso el valor ASCII del canicter y en el
byte de mayor peso el c6digo del teclado (scan code). Si el byte de menor
peso es 0 la tecla pulsada corresponde a una tecla para el movimiento del
cursor 0 a una tecla de funci6n. Si el servicio es 1 retorna un 0 si el buffer
esta vacio, si no retorna el siguiente caracter en el buffer. Si el servicio es
2 retorna el estado de las teclas tipo shift cuando estan activadas (1 Shift
derecho, 2 Shift izquierdo, 4 Ctrl, 8 Alt, 16 Scroll Lock, 32 Num Lock,
64 Caps Lock, 128 Ins).
Permite conocer la cantidad de memoria que tiene el ordenador. Este ser-
vicio se activa con la interrupci6n 18 (INT OxI2).
Retorna el maximo numero de bloques contiguos de memoria de lK
presentes.
unsigned _bios_printer(unsigned servicio, unsigned impresora,
unsigned dato);
Manda un unico octeto a la impresora el cual se corres-
ponde con el byte de menor peso de dato.
Informa del estado de la impresora. EI argumento dato
es ignorado.
EI byte de menor peso del valor retornado conti ene, para todos los
servicios, el estado de la impresora (1: impresora apagada, 8: error de E/S,
16: impresora seleccionada, 32: no hay papel).
# include <stdio.h>
# include <bios.h >
main( )
{
unsigned vr, dato = 0;
enum {LPn, LPT2, LPT3, PRN=O};
enum {ESCRIBIR, INICIALIZAR, ESTADO };
vr = _bios--printer(ESTADO, LPT1, dato),o
printj("Impresora LPT1, estado: % # 04x  n'~ vr),o
mensaje_error(vr & OxOOFF),o
vr = _bios--printer(INICIALIZAR, LPT1, dato),o
printj(" nLPT1 inicializada, estado: % #04x n'~ vr),o
mensaje_error(vr & OxOOFF),o
dato = '/',o
vr = _bios--printer(ESCRIBIR, LPT1, dato),o
printj("  nLPT1 trabajando, estado: % # 04x  n'~ vr),o
mensaje_error(vr & OxOOFF),o
]
void mensaje_error(unsigned vr)
[
char *msg[ J= [ "bit 0: impresora apagada'~
"bit 1: no utilizado'~
"bit 2: no utilizado'~
"bit 3: error de E/S'~
"bit 4: impresora sefeccionada'~
"bit 5: no hay papel'~
"bit 6: respuesta de fa impresora':
"bit 7: impresora no ocupada"
];
int i;
unsigned test = Ox0001;
if (vr != 0)
for (i = 0; i < 8; i+ +)
if (vr & (test < < i))
printj("%s  n'~ msg[i]);
Impresora LPTl, estado: Ox30
bit 4: impresora seleccionada
bit 5: no hay papel
LPTI inicializada, estado: Ox30
bit 4: impresora seleccionada
LPTl trabajando, estado: Ox31
bit 0: impresora apagada
bit 4: impresora seleccionada
bit 5: no hay pape1
unsigned _bios_serialcom(unsigned servicio, unsigned puerto,
unsigned dato);
Inicializa el puerto de comunicaciones (velocidad en
baudios, longitud del dato, bits de parada y paridad)
dato Baudios Bits dato Bits Stop Paridad
0 110 ninguna
2 7
3 8
4 2
dato Baudios Bits dato Bits Stop Paridad
8 impar
24 par
32 150
64 300
96 600
128 1200
160 2400
192 4800
224 9600
unsigned r, data = 0;
enum ( COM1, COM2, COM3 ];
data = 160 I 2 I 4 I 0;
r = _bias---.Serialcam(O, COM1, data);
Este ejemplo inicializa el puerto 1con los siguientes valores: 2400 bau-
dios, dato de 7 bits, 2 bits de stop y no paridad.
Esta funci6n retorna un entero. El byte de mayor peso, contiene infor-
maci6n de estado y el byte de menor peso depende del servicio.
Cad a bit del byte de mayor peso indica:
registro de desplazamiento de transmisi6n, vado
registro de datos, vado
interrupci6n detectada (break)
error de transmisi6n (encuadre)
error de paridad
error de transmisi6n (se han traspasado los limites)
dato listo
Si el servicio es 1, el bit 15 seria puesto a 1 si el dato no pudiera ser
enviado.
Si el servicio es 2, el dato leido seria devuelto en el iJyte de menor peso.
Si ocurre un error, al menos un bit del byte de mayor peso seria puesto
a 1 para indicar la procedencia del error.
Si el servicio es 0 0 3, en el byte de menor peso se da una informacion
adicional.
DCD (detecci6n de portadora de datos)
RI (tonos de Hamada en linea)
DSR (datos preparados)
CTS (listo para enviar)
Cambio en DCD
Cambio en RI
Cambio en DSR
Cambio en CTS
Los servicios con respecto a la hora del dia se activan con la interrupcion
26 (INT Oxla).
Hay dos servicios: 0 para leer la hora y 1 para poner la hora. La hora
hay que especificarla en pulsos de reloj transcurridos desde la 0 horas. Un
segundo equivale aproximadamente a 18.2,Pulsos de reloj.
Ejemplo:
# include <stdio.h>
# include <bios.h >
main( )
{
static char bujjer[1024];
void _far *p = (char _far *)bujjer;
struct diskinjo_t disco = { 0, 0, 0, 1, 1, p ];
int i, valor, minutos, horas;
long tiempo;
for (i = 0; i < 4; i+ +)
{
disco.drive = i;
_bios_disk(O, &disco);
if ((valor = _bios_disk(2, &disco)) & OxFFOO)
printf(Herror: E/S disco %d - estado: % # 06x  n': i, valor);
valor = _bios_equiplist( );
printf(H  nn 0 de puertos serie: %d  n': (valor & OxOeOO)> > 9);
printf(H  npulse una tecla para continuar  n ");
valor = _bios_keybrd(O);
if (valor & OxOOjf)
[
printf(H  ncardcter introducido %c  n': valor & OxOOjj);
printf(Hc6digo de teclado %d n': (valor & OxjjOO)> > 8);
I
_bios_timeojday(O, &tiempo);
tiempo = (tiempo*10)/182;
minutos = tiempo/60;
horas = minutos/60;
minutos % = 60;
printf(H  nhora: % 02d:%02d  n': horas, minutos);
]
error: E/S disco 1 - estado: Ox8000
error: E/S disco 2 - estado: Ox0101
error: E/S disco 3 - estado: Ox0101
canicter introducido
c6digo de teclado 57
C permite ejecutar funciones relacionadas con el sistema operativo como
chdir( ), mkdir( ), rmdir( ), getcwd( ) y system ( ). De estas, tiene especial
interes la funcion system ( ) ya que nos permite ejecutar cualquier fichero
.COM, .EXE 0 .BAT, con 10 que tenemos acceso desde un pragrama C a
cualquier orden del DOS.
Cuando se da formate a un disco desde el DOS, se crea un directorio que
recibe el nombre de directorio raiz (  ). Las entradas en este directorio pue-
den ser ficheras 0 bien otros directorios (llamados subdirectorios). Estos
ultimos, a su vez, pueden contener ficheros 0 bien subdirectorios, y asi su-
cesivamente.
Es una secuencia de uno 0 mas nombres de directorios, separados por .
Esta secuencia puede finalizar con un nombre de fichera. La sintaxis es:
El primer canicter  es el nombre del directorio raiz. El directorio
activo puede ser referenciado por "." y el directorio padre del activo por "..".
Se denomina directorio activo al directorio en el que estamos traba-
jando. Se dice que un directorio A es padre de otro B, si B es subdirectorio
de A.
La siguiente figura (arbol) muestra un ejemplo de como puede ser la
estructura de ficheros de un disco.
 (directorio raiz)
I
En esta figura hemos representado los directorios con mayusculas y
10s ficheros con minusculas. C es el directorio padre de los subdirectorios
FICHEXE, FICHINC y EJEMPWS.
Suponiendo que estamos en la unidad C, el camino para acceder al
fichero ejOl.c es:
Este otro camino conduce hasta el directorio C. Se asume la unidad
de disco en la que estamos trabajando.
El siguiente camino conduce hasta el directorio padre del activo. Por
ejemplo, si estamos en C, nos lleva hasta el directorio rafz.
Significa que se puede enviar la informacion producida por una orden cual-
quiera, no solamente a la salida estandar, sino a un fichero cualquiera, uti-
lizando el sfmbolo > en la linea de ordenes. Por ejemplo:
Esta orden crea el fichero INDICE si no existe, y almacena la infor-
macion suministrada por la orden DIR en el.
Con esta orden grabamos la informacion suministrada por DIR, a con-
tinuacion de la ya existente en INDICE.
Es posible obtener la informacion de entrada para una orden no solamen-
te del teclado, sino de un fichero cualquiera utilizando el sfmbolo < en
la linea de ordenes. Por ejemplo:
Con esta orden clasificamos los datos del fichero ALUMNOS y el re-
sultado es enviado al fichero ALUM.CLA.
Cuando queremos procesar el resultado de una orden con otra orden se
utiliza el simbolo I (ASCII 124). Por ejemplo:
Esta linea de 6rdenes visualiza en pantalla el resultado dado por la
orden DIR (directorio) clasificado en orden alfabetico.
Se denomina prompt al indicativo que se visualiza en pantalla cuando se
tiene cargado el sistema operativo. Por defecto, el prompt esta formada
por el nombre de la unidad de disco en la que estamos trabajando y el sim-
bolo >.
A > indica que estamos en la unidad A
B> indica que estamos en la unidad B
C> indica que estamos en la unidad C
Para ir de una unidad a otra, se escribe el nombre de la unidad a la
que queremos ir seguida de dos puntos y se puisa Enter. Por ejemplo:
Ademas de saber en que unidad estamos, es importante saber tambien
en que directorio de esa unidad nos encontramos trabajando. La orden del
DOS que nos facilita permanentemente esta informaci6n es:
Por ejemplo, de acuerdo con la estructura en arbol presentada ante-
riormente y despues de haber ejecutado PROMPT $p$g, si estamos en el
directorio EJEMPLOS de la unidad C, el prompt que se visualiza en pan-
talla es:
Observar el prompt de estos ejemplos (directorio en el que estamos)
para comprender la orden que se quiere ejecutar.
Cuando se quiere pasar un nombre de fichero (path-name) a un proceso
en una Hamada exec( ) 0 en una funci6n, como par ejemplo la funci6n
system(), se debe utilizar como delimitador una doble barra invertida (   )
en lugar de una sola barra invertida ().
r = system("DIR C:   C600  SOURCE");
execl("bin/prog': "prog': "sub': "bin   xxx': NULL);
EI sistema operativo UNIX utiliza como delimitador I en lugar de 
que utiliza.MS-DOS. No obstante, MS-DOS reconoce como delimitador /
en aquellos lugares donde se espera un path. En un lugar donde no es es-
perado hay que utilizar   .
Ellenguaie C dispone de las funciones que a continuaci6n se exponen para
control de directorios:
# include <direct.h >
# include <errno.h >
La funci6n chdir( ) devuelve el valor 0 si se ejecuta satisfactoriamente
y un valor -1 si ocurre un error.
Este ejemplo, suponiendo que estamos en la unidad B, conduce hasta
el directorio PROGINC de la unidad de disco B. Tambien podria haberse
especificado el camino HB:IPROGINC':
main( )
{
if ( chdir(Hc:   c  jichexe") 0 )
system(Hdir");
else
printft tError' ');
Este ejemplo conduce hasta el directorio FICHEXE de la unidad C
y lista su contenido.
# include < direct.h >
# include <errno.h >
La funci6n mkdir( ) devuelve el valor 0 si se ejecuta satisfactoriamen-
te y un valor -1 si ocurre un error.
main( )
{
if ( mkdir("c:   word   ayuda") -1 )
puts("Error: directorio no creado");
else
puts("Directorio creado");
# include <direct.h >
# include <errno.h >
La funcion rmdir( ) devuelve el valor 0 si se ejecuta satisfactoriamente
y un valor distinto de 0 si ocurre un error.
main( )
{
if ( rmdir(Hc:   word   ayuda") = = -1 )
puts(HError: directorio no borrado");
else
puts(HDirectorio borrado");
Esta fund on almacena en la variable buffer el camino (path) correspon-
diente al directorio detrabajo actual. EI argumento n especifica la longi-
tud maxima para el nombre del camino. Si la longitud maxima de este es
superior a n se obtiene un error.
# include <direct.h >
# include <errno.h >
La fundon getcwd( ) devuelve un puntero al camino (path) obtenido
o un NULL para indicar que ha ocurrido un error.
# include <stdio.h>
# include <direct.h >
main( )
[
char camino_actual[N], camino[N];
getcwd( camino_actual, N );
puts( '~ directorio ?" );
gets( camino );
chdir( camino );
system( "dir *.exe" );
chdir( camino_actual );
J
Entrada:
Salida:
c:  c  bin
ficheros .exe del directorio bin
Cuando en un programa Cencontramos una Hamada a esta fund6n, se
ejecuta la orden DOS espedficada por cadena y al finalizar la ejecuci6n
se continua en la siguiente sentencia del programa. El argumento cadena
puede ser un programa .EXE, .COM 0 .BAT.Si el valor de cadena es NULL
la fund6n simplemente chequea si esta presente el interprete de 6rdenes
COMMAND.COM.
# include < direct.h >
# include <process.h >
# include <errno.h >
La funci6n system () devuelve un valor distinto de cero si cadena vale
NULL y COMMAND.COM existe y un valor 0 si COMMAND.COM no
existe.
Si cadena tiene un valor distinto de NULL la funci6n system( ) de-
vuelve el valor 0 si se ejecuta satisfactoriamente y un valor -1 si ocurre un
error.
Cuando se ejecuta esta funci6n se carga y ejecuta una nueva copia de
COMMAND.COM, por 10 que system ( ) no puede ser utilizada para mo-
dificar las variables de entorno actuales.
# include <stdio.h>
# include <stdlib.h>
#dejine N 81
main( )
(
FILE *pjichero;
char bujjer[N];
system("cls");
/ * Abrir el jichero alumnos para escribir */
if ((pjichero = jopen("alumnos': "w")) = = NULL)
(
printj("EI jichero alumnos no puede abrirse.  n");
exit(l);
l
printf("Pulse AZ para jinalizar  n  n");
printf("Alumno: ");
while (fgets(bujjer, N, stdin) != NULL)
(
jputs(bujjer, pjichero);
printf("  nAlumno: ");
l
jclose(pjichero); / * cerrar el jichero */
/ * Clasijicar el jichero */
system(Hsort < alumnos > alum");
system(Hcopy alum alumnos");
system(Hdel alum ");
/ * Abrir el jichero alumnos para leer */
if ((pjichero = jopen(Halumnos': Hr")) = = NULL)
(
printj(HEI jichero alumnos no puede abrirse.  n");
exit(l);
}
/ * Escribir el contenido del jichero */
system(Hcls");
while (fgets(bujjer, N, pjichero) != NULL)
printj(H%s  n': bujjer);
jclose(pjichero); / * cerrar el jichero */
}
Este ejemplo crea el fichero alumnos, 10 clasifica y envia el resultado
al fichero alum, copia alum en alumnos y borra alum. Ahora se tiene el
fichero alumnos clasificado, que se visualiza en pantalla.
Esta linea da lugar a que se cree el fichero INDICE con el contenido
clasificado del directorio.
Esta funci6n determina si el fichero especificado por path existe y si puede
ser accedido en el modo especificado.
# include <io.h >
# include <errno.h >
La funci6n access( ) devuelve un valor 0 si el fichero tiene el modo
especificado. Si el fichero no existe 0 si no es accesible en el modo especifi-
cado, la funci6n devuelve un valor -1 y la variable errno es puesta al valor
correspondiente.
Esta funci6n cambia el permiso que tiene el fichero especificado por path
por el especificado por pmodo. El significado de este argumento es el mis-
mo que se ha expuesto en la fund6n open( ).
#include <sys  types.h >
# include <sys  stat.h >
# include <io.h >
# include <errno.h >
La funci6n chmod( ) devuelve un valor 0 si el permiso es cambiado.
Un valor -1 indica un error.
1************ Cambiando los atributos de un jichero ************1
1* CCHMOD.C *1
# include <stdio.h>
# include <stdlib.h>
# include <conio.h >
# include <io.h >
# include <sys  types.h >
# include <sys  stat.h >
# include <sys  utime.h >
main(int argc, char ~rgv[ J)
[
1* Chequear si el jichero existe *1
if (!EXISTE(argv[IJ))
[
printj("Pormato: CCHMOD nombre");
exit(l);
J
1* Chequear si el jichero tiene permiso de escritura *1
if ( !access(argv[l], ESCRIBIR))
[
printj("Pichero %s. Atributos: LEERIESCRIBIR  n': argv[IJ);
printj('~ Cambiar atributos a LEER ? (sin) ");
if (tolower(getche( )) = = 's')
chmod(argv[l], S-.1READ);
J
else
[
printj("Pichero %s. Atributos: LEER  n': argv[IJ);
printf("" Cambiar atributos a LEERIESCRIBIR ? (sin) ");
if (tolower(getch()) = = 's')
chmod(argv[l], S-.1READ I S-.1WRITE);
printf(Ha fecha actual ? (sin) ");
if (tolower(getche( )) = = <s')
utime( argv[l], NULL );
Esta funci6n extiende 0 trunca a la longitud especificada por n el fichero
cuyo numero asociado es num. El fichero debe ser abierto en un modo que
permita escribir. Si e1fichero es extendido se afiaden caracteres nulos ('  0').
# include <io.h >
# include <errno.h >
La funci6n chsize() devuelve un 0 si el fichero es cambiado. Un valor
-1 indica un error y la variable errno es puesta al valor correspondiente.
int nf;
nf = open(Hdatos': O-RDWR, S--1READ I S--1WRITE);
# include <stdio.h >
# include <io.h >
# include <errno.h >
La funci6n unlink( ) devuelve un 0 si el fichero es borrado. Un valor
-1 indica un error y la variable errno es puesta al valor correspondiente.
if (unlink("datos") = = -1)
perror("No se puede borrar el jichero");
else
printjt jichero borrado ");
Esta fundon renombra el fichero 0 directorio especificado por pf_actual
con el nombre especificado por pf_nuevo. Esta funcion puede tambien
ser utilizada para mover un fichero a otro directorio especificado por
pf_nuevo. Sin embargo, un fichero no puede ser movido de un dispositi-
vo a otro, por ejemplo de la unidad A a la unidad B.
# include < stdio.h >
# include < io.h >
# include <errno.h >
La fundon rename( ) devuelve un 0 si el fichero es renombrado. Un
valor distinto de 0 indica un error y la variable errno es puesta al valor
correspondiente.
Esta funcion asigna el modo texto (0_TEXT) 0 el modo binario
(O_BINARY) al fichero cuyo numero asodado es nurn.
# include <fcntl.h >
# include <io.h >
# include <errno.h >
La funci6n setmode( ) devue1veel modo anterior. Un valor -1 indica
un error y la variable errno es puesta al valor correspondiente.
Esta funci6n normalmente es utilizada para modificar el modo por
defecto (O_TEXT) de los ficheros estandar.
Esta funci6n obtiene informaci6n acerca del fichero 0 directorio especifi-
cado por path y la almacena en la estructura de tipo stat apuntada por buffer.
#include <sys  types.h >
# include <sys  stat.h >
# include <errno.h >
[
dev_t st_dev;
ino_t st_ino;
unsigned short st_mode;
short st--fllink;
short st_uid;
short st---f5id;
1* caracterfsticas: directorio,
fichero, permisos LIE etc. *1
1* siempre es 1 *1
I * solo para UNIX *1
I * solo para UNIX *1
dev_t st_rdev;
oif_J sL-size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
];
/ * igual que st_dev */
/ * tamafio del jichero en bytes */
/ * ultima vez que se modifico */
/ * igual que st_atime */
/ * igual que st_atime */
La funci6n devuelve un 0 si no hay error. Un valor -1 indica un error
y la variable errno es puesta al valor correspondiente.
struct stat p;
stat(Hdatos': &p);
printftCModificado el: ': ctime(p.st_atime));
Esta funci6n determina si num esta asociado con un dispositivo (un termi-
nal, consola, impresora 0 puerto serie).
La funci6n isatty( ) devuelve un valor distinto de 0 si num correspon-
de a un dispositivo tipo caracter y 0 en caso contrario.
int n;
n = isatty(fileno(stdout));
Esta funci6n cambia la fecha y hora en la que fue modificado por ultima
vez el fichero especificado por path. El fichero tiene que tener acceso para
escribir. La estructura t contiene la fecha del ultimo acceso y la fecha de
la ultima vez que fue modificado el fichero. Bajo MS-DOS la fecha del
ultimo acceso no tiene sentido, por 10 que este concepto es ignorado. Si
t es NULL la modificaci6n se hace d(; acuerdo con la fecha y hora actuales.
# include <sys  types.h >
# include <sys  utime.h >
# include <errno.h >
La funci6n utime( ) devuelve un cero si la modificaci6n es hecha. Un
valor -1 indica un error y la variable errno es puesta al valor correspondiente.
if (utime(''lc/source/datos': NULL) -1)
perror('jecha no modijicada");
else
printf('jecha modijicada ");
Cuando la funci6n main( ) retorna al DOS puede utilizarse una orden de
proceso por lotes IF para verificar el c6digo de salida retornado. Por ejemplo:
El resultado de la orden anterior sera verdad si el c6digo de salida es
mayor 0 igual que 1 ( > = 1 ).
El resultado de la orden anterior sera verdad si el c6digo de salida es
menor que 1 ( < 1 ).
El 80x86 soporta 256 interrupciones diferentes identificadas cada una de
ellas por un numero entre OOHy FFH. Las direcciones segmentadas de las
256 rutinas de tratamiento de la interrupci6n estan almacenadas en una
tabla de vectores de interrupci6n que comienza en la direcci6n OOOO:OOOOH.
Cuando se da una interrupci6n el control del ordenador es pas ado a una
subrutina de tratamiento de la interrupci6n, que a menudo esta almacena-
da en la ROM del sistema, cuya direcci6n de comienzo viene dada por el
vector de interrupci6n correspondiente. Los numeros de interrupciones del
DOS van des de 20H a 3FH.
Las siguientes funciones C manipulan los servicios proporcionados por
el DOS a traves de la interrupci6n Ox21. En general, todas las funciones
de la interrupci6n Ox21 se llaman ejecutando la interrupci6n Ox21 con un
numero de funci6n en el registro AH. La mayoria de ellas devuelven un
c6digo de finalizaci6n en los registros AL 0 AX.
Las funciones prototipo que se exponen a continuaci6n se encuentran
dec1aradas en el fichero dos.h.
El control es pasado de una rutina de interrupci6n a otra (vect_int) como
si esta otra rutina de interrupci6n hubiera side llamada originalmente.
Asigna un bloque de memoria utilizando la funci6n Ox48 del DOS, t es
el tamaiio en bloques de 16 bytes y seg es la direcci6n del segmento donde
va a ser ubicado el bloque. La funci6n retorna un valor 0 si se ejecuta satis-
factoriamente. Un valor distinto de 0 indica un error.
Esta funci6n cierra el fichero que tenga como descriptor num utilizando
la funci6n Ox3Edel DOS. La funci6n retorna un valor 0 si se ejecuta satis-
factoriamente. Un valor distinto de 0 indica un error.
Crea un fichero utilizando la funci6n Ox3C del DOS. Si el fichero existe
se borra. jich es el nombre del fichero, atrib son los atributos dados al fi-
chero (-A_NORMAL, -A_RDONLY, -A_HIDDEN, -A_SYS-
TEM, -A.- VOLID, -A_SUBDIR, -A-ARCH) y num es un puntero
a una variable que contiene el descriptor del fichero. Verla funci6n creat().
La funci6n retorna un valor 0 si se ejecuta satisfactoriamente. Un valor
distinto de 0 indica un error.
Crea un fichero utilizando la funci6n Ox5Bdel DOS. Si el fichero existe
se obtiene un error.
unsigned _dos_findfirst(const char *jich, unsigned atrib,
struct find_t *injo-fich);
Libera un bloque de memoria asignado por __dos_allocmen() utilizand
la funci6n Ox49del DOS. La funci6n retorna un valor 0 si se ejecutasati
factoriamente. Un valor distinto de 0 indica un error.
Encuentra la primera entrada de directorio que concuerde con los datos
especificados utilizando la funci6n Ox4E del DOS. injo-fich es una es-
tructura de tipo find_t declarado en dos.h, que contiene informacion sa-
bre el fichero. La funci6n retorna un valor 0 si se ejecuta satisfactoriamen·
te. Un valor distinto de 0 indica un error.
Encuentra la siguiente entrada de directorio que concuerde utilizandola
funci6n Ox4F del DOS.
Obtiene la fecha utilizando la funcion Ox2A del DOS, fecha es una estruc-
tura de tipo dosdate_t declarado en dos.h, que contiene los datos relati-
vos a la fecha.
Obtiene la unidad de disco actual (A: = 0, B:
Ox19 del DOS.
Obtiene el espacio libre del disco utilizando la funcion Ox36 del DOS, drv
es la unidad de disco y esp es un puntero a una estructura de tipo diskfree_t
declarado en dos.h, que contiene los datos relativos al espacio en disco.
La funcion retorna un valor 0 si se ejecuta satisfactoriamente. Un valor
distinto de 0 indica un error.
Obtiene los atributos actuales del fichero 0 directorio utilizando la fun-
cion Ox43 del DOS. La funcion retorna un valor 0 si se ejecuta satisfacto-
riamente. Un valor distinto de 0 indica un error.
Obtiene la fecha y hora asociadas con el fichero utilizando la funcion Ox~7
del DOS. La funcion retorna un valor 0 si se ejecuta satisfactoriamente.
Un valor distinto de 0 indica un error.
Obtiene la hora utilizando la funcion Ox2C del DOS, hora es una estructu-
ra de tipo dostime_t declarado en dos.h, que contiene los datos relativos
a la hora.
Obtiene el vector de interrupcion asociado con el numero de interrupcion
especificado utilizando la funcion Ox35 del DOS.
Instala un programa residente en memoria (TSR) utilizando la funcion Ox31
del DOS, cod es el codigo de salida y t es el numero de bloques de 16bytes
necesarios para instalar el programa. El espacio en excesoes devuelto al DOS.
Abre un fichero utilizando la funcion Ox3Ddel DOS. Verla funcion open( ).
La funcion retorna un valor 0 si se ejecuta satisfactoriamente. Un valor
distinto de 0 indica un error.
unsigned _dos_read(int num, void _far *buffer, unsigned c,
unsigned *b);
Lee datos de un fichero utilizando la funcion Ox3F del DOS. Ver la fun-
cion read( ). num es el descriptor del fichero, buffer almacena los bytes
leidos, c es el numero de bytes a leer y b es el numero de bytes actualmente
!eidos. La funci6n retorna un valor 0 si se ejecuta satisfactoriamente. Un
valor distinto de 0 indica un error.
Cambia el tamafio de un bloque de memoria, previamente asignado por
_dos_allocmen, utilizando la funcion Ox4Adel DOS, t es el nuevo tam a-
fio, seg es direccion del segmento del bloque de memoria y tm es un punte-
ro a un entero que indica el tamafio maximo que puede ser asignado. La
funcion retorna un valor 0 si se ejecuta satisfactoriamente. Un valor distin-
to de 0 indica un error.
Establece la fecha utilizando la funcion Ox2Bdel DOS, fecha es una es-
tructura de tipo dosdate_t declarado en dos.h, que contiene los datos re-
lativos a la fecha.
Selecciona la unidad de disco utilizando la funcion OxOEdel DOS, drv es
O=unidad actual, 1=A:, 2=B:, etc.; num_drv es un puntero a un entero
que indica el numero total de unidades.
unsigned _dos_setfiJeattr(const char *path, unsigned atrib);
:~tablece los atrib.~tos del fichero 0 directorio utilizando la funcion Ox43
U
e DOl S.d~a .funclOn retorna un valor 0 si se ejecuta satisfactoriamente
n vaor lstmto de 0 indica un error. .
unsigned _dos-setftime(int num, unsigned fecha, unsigned hora);
Establece la fecha y hora asociadas con el fichero utilizando la funcion Ox57
del DOS. La funcion retorna un valor 0 si se ejecuta satisfactoriamente.
Un valor distinto de 0 indica un error.
Establece la hora utilizando la funcion Ox2Ddel DOS. hora es una estruc-
tura de tipo dostime_t declarado en dos.h, que contiene los datos relati-
vas a la hora.
void _dos_setvect(unsigned num_int,
void ( ~nterrupt _far *pint)( »;
Establece el vector de interrupcion (pint) para un numero de interrupcion
(num_int) especificado, utilizando la funcion Ox25 del DOS.
unsigned _dos_write(int num, void _far *buffer,
unsigned c, unsigned *b);
Escribe datos en un fichero utilizando la funcion Ox40del DOS. Ver la fun-
cion writer ). num es el descriptor del fichero, buffer contiene los bytes a
escribir, c es el numero de bytes a escribir y b es un puntero al numero de
bytes actualmente escritos. La funcion retorna un valor 0 si se ejecuta sa-
tisfactoriamente. Un valor distinto de 0 indica un error.
Obtiene informacion extendida de un error utilizando la funcion Ox59del
DOS. info_error es un puntero a una estructura de tipo DOSERROR de-
clarado en dos.h, que contiene informacion relativa al error ocurrido.
Sustituye una rutina de tratamiento de un "error critico" de la interrup-
ci6n Ox24 hecha a medida (pint) por la que proporciona el DOS. El retor-
no desde esta rutina puede producirse desde una sentencia return, desde
una funci6n Jardresume( ) 0 desde una funci6n _hardretn( ).
Retorna al DOS despues de un error de hardware. resu puede tener uno
de los siguientes valores: _HARDERR_IGNORE, _HARDERR_RE-
TRY, _HARDERR~BORT, _HARDERR_FAIL.
Retorna al programa despues de un error de hardware. error es un c6digo
de error de DOS.
Segun hemos visto la funci6n Ox31 de la interrupci6n Ox21 (0 su eqpivalen-
te INT Ox27) proporcionan el mecanismo para dejar residente un progra-
ma en memoria (TSR). Normalmente un programa residente consta de dos
partes: la parte residente que queda en memoria y la parte transitoria que
instala e inicializa la parte residente y no queda en memoria.
Escribir la hora en la esquina superior izquierda de la pantalla utili-
zando un programa residente. Esto nos permitini ejecutar simultaneamen-
te cualquier otro proceso.
Para realizar este ejemplo utilizaremos ademas de la funci6n Ox31, la
interrupci6n OxIC ("tic" del temporizador). La interrupci6n de software
OxIC es invocada por la interrupci6n Ox08 cada vez que esta se produce.
El temporizador del sistema emite la interrupcion Ox08 cada 1/18.2 segun-
dos con el fin de actualizar el contador de tiempos. Por ello, la interrup-
cion Ox08 recibe el nombre de "tic".
Segun 10 expuesto, una rutina del usuario apuntada por el vector de
interrupcion correspondiente al numero de interrupcion OxIC se ejecutara
por cad a "tic" del contador de tiempos.
Siguiendo el mecanisme descrito, el programa correspondiente al pro-
blema planteado, es el siguiente:
# include <dos.h >
# include <time.h >
# define NUM~NT OxlC
void (_cdecl ~nterrupt _far *pint)(void);
void _cdecl ~nterrupt _far mi_int(void);
_segment segvar = OxB800; / * Para mono sustituir por OxBOOO*/
int i, _based(void) *PV;
char hora[9];
int atributo = Ox7000;
unsigned long tics;
/ * hora del sistema: hh:mm:ss  0 */
/ * video inverso: byte alto = Ox70 */
int main(void)
{
/ * Salvar el vector de interrupci6n de la INT OxlC */
pint = _dos~etvect(NUM~NT);
/ * Hora inicial; hora = hh:mm:ss  0 */
---.Strtime(hora);
/ * Poner en la interrupci6n Oxle,
* el vector de interrupciones mi_int
*/
_dos---.Setvect(NUM~NT, mi_int);
1* Visualizarlguardar la hora inicial en vkieo inverso *1
for (i= 0, pv = (int _based(void) *)144;i <8; i+ +, pv + +)
{
carli] = hora[i] I atributo;
*(segvar:>pv) = car[i];
1* Instalar nuestra rutina de interrupci6n (TSR) *1
_dos_keep(O, 256); 1* (4K*1024)/16 = 256 pdrrajos *1
1* usar la utilidad EXEHDR d
void _cdecl ~nterrupt _far mi_int(void)
{
tics+ +; h 1 segundo = 18.2tics *1
if (!(tics % 18L))
(
_enable( );
1* Actualizar la hora *1
-strtime(hora);
1* Actualizar los segundos *1
car[7] hora[7] I atributo;
car[6] = hora[6] I atributo;
1* Actualizar los minutos *1
if (hora[6] = = '0')
{
car[4] = hora[4] I atributo;
car[3] = hora[3] I atributo;
if (hora[3] = = '0')
{
1* Actualizar las horas *1
car[l] = hora[l] I atributo;
carrO] = hora[O] I atributo;
J
J
/ * Visualizar la hora */
for (i= 0, pv = (int _based(void) *)144; i<8; i+ +, pv + +)
*(segvar:>pv) = ear[i];
}
/ * Pasar el control a la [NT Ox1C */
_ehain~intr(pint);
}
La fund6n _dos_keep( ) asegura que la rutina de interrupci6n
mi_int( ) permanezca instalada en memoria hasta que el vector de inte-
rrupci6n correspondiente a la INT OxIC sea reemplazado 0 hasta que se
reinicialice e1sistema.
Mientras mi_int no es invocada el cursor queda libre (no se esta es-
cribiendo el re1oj), 10 que permite disponer del prompt del DOS y ejecutar
cualquier otro proceso.
Cada program a que es ejecutado por el sistema operativo debe ser primero
transformado en un proceso. Un proceso es una unidad ejecutable, a la que
el sistema operativo Ie ha asignado recursos tales como memoria princi-
pal, memoria secundaria, dispositivos perifericos y UCP.
Con el DOS, los conceptos de programa y proceso son intercambia-
bles, porque el DOS es un sistema operativo monotarea; esto significa que
s610 es posible ejecutar un proceso cada vez. Esta circunstancia no se da
en un sistema operativo multitarea como OS/2 0 como UNIX.
En el DOS, la iniciaci6n, ejecuci6n y finalizaci6n de un proceso es res-
ponsabilidad del interprete de 6rdenes COMMAND.COM. Cuando esto
ocurre asi COMMAND.COM actua como proceso padre y el proceso ini-
ciado recibe el nombre de proceso hijo. Un proceso hijo puede a su vez
actuar como padre e iniciar otro proceso hijo. Las funciones que vamos
a exponer en este capitulo tienen como finalidad la iniciaci6n, ejecuci6n
y finalizaci6n de un proceso.
La iniciaci6n de un proceso bajo DOS, lleva consigo la ejecuci6n de los
siguientes pasos:
1. Asignaci6n del espacio en memoria necesario para cargar el pro-
ceso. Un espacio insuficiente hace que el programa no sea ejecu-
tado. La distribuci6n de este espacio es de la forma siguiente:
2. Se construye el prefijo del segmento del programa (PSP). Este tiene
un tamafio de 256 bytes y no tiene nada que ver con la cabecera
del fichero almacenado en el disco. El DOS coloca en esta zona
una informaci6n indispensable, entre la que cabe destacar:
• El c6digo de instrucci6n de INT Ox20 que provoca el retorno
al programa llamador (generalmente este programa es COM-
MAND.COM). Esta interrupci6n restaurani los vectores de in-
terrupci6n Ox22, Ox23 y Ox24 con el valor original que tenian
antes de comenzar la ejecuci6n del proceso.
• Primera direcci6n segmentada por encima de la memoria asig-
nada al programa. Un programa puede utilizar esta direcci6n
para determinar el tamafio real de la memoria que tiene asignada.
• Llamada al planificador de funciones del DOS. Su funci6n es
ejecutar la rutina de servicio correspondiente al numero de fun-
ci6n que Ie es pasado.
• La direcci6n a la que se pasani el control cuando la ejecuci6n
del programa finalice con una llamada a la INT Ox20. Este va-
lor se copia en el vector de interrupci6n Ox22.
• La direcci6n a la que se pasani el control en caso de un Ctrl +C.
Este valor se copia en el vector de interrupci6n Ox23.
• La direcci6n a la que se pasani el control en caso de un "error
fatal". Este valor se copia en el vector de interrupci6n Ox24.
• 20 octetos que contienen los descriptores (0 a 19) para manejo
de los ficheros abiertos. Los 5 primeros estan normalmente asig-
nados y se corresponden con los dispositivos estandar de E/S.
• Direcci6n del bloque que contiene las variables de entorno. En
C esta informaci6n es gestionada por la funci6n getenv( ) y pu-
tenv( ).
• Parametros de la linea de 6rdenes tecleados por el usuario cuando
invoc6 al proceso (128 bytes). En C argc y argv permiten pasar
estos parametros a la funci6n main(). Este area sirve igualmente
como area de transferencia del disco por defecto, que el DOS
utilizara en la E/S de ficheros.
El PSP da una entidad propia al proceso. Entre otras cosas asegura,
indistintamente de que el proceso termine normalmente 0 no, que el DOS
recupere el control sobre los recursos que han sido asignados a dicho proceso.
Cuando un proceso comienza a ejecutarse pueden tomarse acciones que
alteren su normal desarrollo. Tales acciones pueden ser las siguientes:
1. Saltos no locales. Cuando ocurre un saIto de una funci6n a otra
esta claro que hay que salvar en la pila el estado de la funci6n ac-
tual (setjmp( )). Esto permitira mas tarde restaurar (longjmp( ))
el estado en el que se encontraba el sistema antes de efectuar el saIto.
SIGABRT
SIGFPE
SIGINT
Terminaci6n anormal
Excepciones en coma flotante
Ctrl +Break 0 Ctrl +C
Las senales de proceso son provistas para manipular ciertas
interrupciones comunes de una manera estandar (signa/( ) y
raiser ). Para estas y otras senales pueden escribirse tambien ruti-
nas no estandar.
3. Funciones ejecutadas antes de que el proceso devuelva el control.
Durante la ejecuci6n de un proceso pueden ser invocadas hasta
32 funciones para ejecutarlas en el caso de que la ejecuci6n del
proceso termine normalmente y antes de que este devuelva el con-
trol (atexit( ) u onexit( ).
4. Iniciaci6n de procesos. Un proceso puede ser liberado para ini-
ciar otro proceso utilizando las familias de funciones execxxx( )
y spawnxxx( ) 0 por la funci6n system( ).
Cuando un proceso finaliza de una forma normal son ejecutadas las si-
guientes tareas:
1. Se ejecutan en orden inverso (LIFO) las funciones invocadas por
atexit( ) u onexit( ).
4. Se cierran todos los ficheros abiertos y se borran todos los fiche-
ros temporales.
5. Se ejecutan todos los procesos de terminaci6n (coma flotante, res-
taurar divisi6n por cero (INT OxOO),restaurar interrupciones so-
lapadas, restaurar INT Ox22, Ox23 y Ox24).
6. Se termina el proceso (INT Ox21,fund6n Ox4C): se libera la me-
moria asignada al proceso, excepto la memoria asignada dimimi-
camente por e1propio proceso, y se retorna un c6digo de salida
al proceso padre.
De las funciones disponibles para terminar un proceso, s610exit() fi-
naliza un proceso normalmente. Las fundones abort( ) y assert( ) s610 in-
c1uyen el punta 5, terminando el proceso de una forma anormal.
Cuando se ejecuta un programa desde el sistema operativo se inicia un pro-
ceso. Utilizando las funciones de control de procesos se puede inidar, eje-
cutar y parar un proceso, desde cualquier otro proceso.
Esta fund6n ejecuta una terminaci6n no normal de un proceso. No des-
carga los buffers de los ficheros y no ejecuta los procesos lanzados por ate-
xit( ) u onexit( ).
#include < stdlib.h >
# include <process.h >
La fund6n abort( ) escribe el mensaje "abnormal program termina-
tion" y devuelve al DOS un c6digo 3.
if ((pj = jopen(argv[argc-l], Ur")) = = NULL)
abort( );
Esta fundon finaliza de una forma normalla ejecudon de un proceso. Antes
de dar por finalizado el proceso la fundon exit( ) ejecuta en orden inverso
(LIFO) las funciones invocadas por atexit( ) u onexit( ), descarga los buf-
fers, cierra todos los ficheros y retorna al proceso padre con un codigo de
salida dado por estado. Esta funcion es preferible a cualquier otra que pueda
realizar una fundon similar.
# include < stdlib.h >
# include < proeess.h >
El argumento estado normalmente es 0 para indicar una salida nor-
mal y otro valor para indicar un error. Esta fundon no retorna un valor.
Esta funcion da lugar a que la fundon referendada por fune sea ejecutada
cuando el programa termina normalmente 0 por via exit(). Pueden ser re-
gistradas un maximo de 32 fundones para ser llamadas sucesivamente. Cuan-
do existen varias llamadas consecutivas a la fundon atexit( ) se ejecutan
desde la ultima a la prim era.
Esta fundon devuelve un 0 si se ejecuta satisfactoriamente 0 un valor
distinto de 0 si ocurre un error.
# include <stdio.h>
# include <stdlib.h>
void fund (void);
void fune2(void);
main( )
(
atexit(fund);
atexit(fune2);
printj("Punci6n principal  n ");
}
void fund (void)
(
printj("Punci6n 1  n ");
}
void fune2(void)
(
printj("Punci6n 2  n");
}
Funci6n principal
funci6n 2
funci6n 1
La funci6n onexit( ) actua igual que la funci6n atexit( ), pero a dife-
rencia de esta, solamente puede utilizarse cuando fune es un puntero a una
funci6n de tipo onexit_t.
Es preferible utilizar la funcion atexit( ) ya que es una funcion C es-
tandar.
Una llamada a esta funcion salva en la variable eDt, el actual entorno de
la pila (conjunto de valores que se guardarian en la pila, por ejemplo, cuando
se llama a una funcion). Una llamada posterior a la funcion longjmp( )
restaura el entorno salvado y devuelve el control a la sentencia que esta
a continuacion de setjmp( ).
La funcion setjmp( ) devuelve un valor 0 despues de salvar el entorno
de la pila; 0 bien, devuelve el argumento valor de longjmp() si a continua-
cion de setjmp( ) se llamo a esta funcion (ver longjmp( ). Si el argumento
valor de longjmp( ) es 0 la funcion setjmp( ) retorna un 1.
Recordar que una sentencia de bifurcacion incondicional goto puede
solamente transferir el control a una etiqueta dentro de la propia funcion.
Por 10 tanto, para ejecutar un salta de una funcion a otra (no local) utilizar
conjuntamente las funciones setjmp( ) y longjmp( ).
Restaura el entorno (eDt) salvado por setjmp( ) y devuelve el control a la
sentencia que esta a continuacion de setjmp( ).
EI argumento valor es un valor distinto de 0 para devolverselo a
setjmp( ).
Los valores de las variables register de la rutina que llam6 a lafunci6n
setjmp( ) no son restaurados despues de ejecutarse la funci6n longjmp().
EI resto de las variables conservan sus valores.
Esta funci6n reinicializa el paquete matematico para operar en coma flo-
tante. Normalmente se utiliza junto con las funciones signal( ), system ( ),
exec( ) 0 spawn( ).
Un programa que utiliza una rutina para manipular errores en coma
flotante (sefial SIGFPE) tiene que recuperarse de un error invocando a
-fpreset( ) y utilizar longjmp( ).
# include <stdio.h>
# include <signal.h >
# include <setjmp.h>
# include <jloat.h>
# include <math.h >
# include <string.h >
jmp_buj marca;
int err_cj;
/ * direcci6n para longjmp */
/ * mimero de error */
void rutina~err _cj(int sig, int num);
void chequear_err(void);
int main(void)
{
double nl, n2, r;
int reL.Jmp;
/ * Instalar la rutina para manipulaci6n de errores
* en coma flotante.
*/
signal(SIGFPE, rutina_err _cf);
/ * Salvar el entorn0 de la pi/a para retornar en caso de error.
* La primera vez, ret-Jmp es 0; se ejecuta if. Si ocurre un
* error ret-Jmp serra puesto a -J y serra ejecutada la
* rutina chequear_err.
*/
ret-Jmp = setjmp(marca);
if (ret-Jmp = = 0)
(
printj(HProbar operaciones invalidas. ");
printj(Hlntroducir dos mimeros: ");
scanft'%/j %/j': &nJ, &n2);
/ * Si en las operaciones siguientes ocurre un error,
* se ejecuta la rutina chequear_err
*/
r = nJ / n2;
printj(H n  n%g / %g
r = nJ * n2;
printj(H n  n%g * %g = %g  n': nJ, n2, r);
}
else
chequear_err( );
/ * Manipulaci6n de fa interrupci6n SIGFPE.
* Error en coma flotante.
*/
void rutina_err _cf(iot sig, iot num)
(
err_cf = num; / *para evitar hacer E/S en la rutina */
/ * Inicializar ef paquete de coma flotante */
-fpreset( );
* a continuacion de setjmp. Devolver el valor -1 para que sea
* falsa la condicion del if.
*/
longjmp(marca, -1);
1
void chequear_err( )
{
char mensaje_err[30];
switch (err_cf)
(
case FPE~NVALID:
strcpy(mensaje_en; "Ntimero invdlido");
break;
case FPE_OVERFLOW:
strcpy(mensaje_err, "Overflow");
break;
case FPE_UNDERFLOW:
strcpy(mensaje_err, "Underflow");
break;
case FPE.-Z,ERODIVIDE·
strcpy(mensaje_err, "Division por cera");
break;
default:
strcpy(mensaje_err, "Error en coma flotante");
break;
1
printj("Error %d: %s  n': err_cj, mensaje_err);
1
Esta funcion permite a un proceso escoger una de varias formas de mani-
pular una serral de interrupcion. La fundon signal( ) indica que se ejecute
la funci6n June cuando durante la ejecuci6n del proceso se de la interrup-
ci6n sig.
void (*signal(int sig, void(*!une)
(int sig[, int subeod))))(int sig);
EI argumento sig es una constante de las siguientes (definidas en
signal.h):
SIGINT 2
SIGFPE 8
SIGABRT 22
Interrupci6n Ctrl +C. Por defecto emite INT Ox23.
Error en coma flotante. Termina el proceso.
Terminaci6n anormal. Termina el proceso. C6digo de
salida 3.
La acci6n que se toma cuando se recibe la seftal de interrupci6n de-
pende del valor de June. EI argumento June debe ser la direcci6n de una
funci6n 0 una constante de las siguientes (definidas en signal.h):
Respuesta por defecto del sistema (equivalente a la lla-
mad a a la funci6n abort( ).
Ignorar la seftal de interrupci6n. No utilizarla con SIGF-
PE ya que el estado del proceso queda indefinido.
Este ejemplo indica que si se produce un error en coma flotante, se
ejecutani la funci6n del usuario mi---fune( ).
Si el argumento June se corresponde con la direcci6n de una funci6n,
entonces dicha funci6n se instala como rutina de manipulaci6n para la se-
nal dada. Si la funci6n realiza un return, el proceso reanuda la ejecuci6n
inmediatamente a continuaci6n del punta en el que se recibi6 la interrupci6n.
Todas las funciones June de manipulaci6n de senales tienen un argu-
mento (sig) y no devuelven un resultado (void), excepto SIGFPE que utili-
za un argumento adicional, subeod, que identifica el tipo de error. Las cons-
tantes validas (FPE-'CXX) para este argumento estan definidas en float.h.
Por ejemplo: FPE---ZERODIVIDE, FPE_OVERFLOW, FPE_SQRT-
NEG, etc.
EI valor de June no es restaurado a su valor despues de recibir la se-
nal. Para recuperarse de un error de coma flotante tenemos que utilizar
conjuntamente las funciones setjmp() y longjmp(). Con respecto a SIGF-
PE, si la funci6n instalada realiza un return, el proceso se reanudaria con
un estado indefinido.
Como las rutinas de manipulaci6n de senales son llamadas asincro-
namente cuando ocurre una interrupci6n, es posible que nuestra funci6n
de manipulaci6n coja el control cuando una operaci6n C se este ejecutan-
do y aun no haya terminado (estado desconocido). Por ello, a este tipo de
rutin as les aplicaremos las siguientes restricciones:
1. No incluir funciones de E/S de bajo nivel 0 funciones incluidas
en stdio.h (por ejemplo printj( ), Jread( ), etc.).
2. No incluir funciones que utilicen directa 0 indirectamente la me-
moria para asignaci6n dinamica (por ejemplo mal/oe(), strdup( ),
etc.).
3. No incluir funciones C que generen llamadas al sistema (por ejem-
plo getewd( ), timer ), etc.).
4. No utilizar la funci6n longjmp( ) a no ser que la interrupci6n sea
originada por un error en coma flotante (per ejemplo el argumento
sig es SIGFPE).
Una selial puesta por signal() no es heredada por un proceso hijo creado
por medio de exec( ) 0 spawn( ).
Esta funci6n envia la selial sig al proceso activo (programa). Si anterior-
mente se ha instalado una rutina de manipulaci6n (funci6n signal( )), en-
tonces la funci6n raise() hace que se ejecute dicha rutina. En caso contra-
rio se ejecutani la acci6n por defecto.
La funci6n raiser) devuelve un 0 si se ejecuta satisfactoriamente 0 un
valor distinto de 0 si no.
# include <stdio.h >
# include <conio.h >
# include <signal.h>
# include <stdlib.h >
# include <dos.h >
# include <bios.h >
void manipular _Ctrl_C(void);
void escribir(char ..str);
iot leer(void);
int main(void)
{
1* Modificar rutina de interrupci6n CTRL +C *1
signal(SIGINT, manipular _Ctrl_C};
prinlf(<cVisualizar el c6digo de la(s) tecla(s) pulsada(s)  n");
do
(
car = getch( );
if (car = = 0)
(
car = getch( );
if (car = = 46) 1* tratar ALT+C igual que CTRL+C *1
raise(SIGINT);
else
printj(<CC6digo extendido: %X n': car);
}
else
printf(<CC6digo ASCIl- %X n': car);
}
while (car /= 27);
}
1* Manipulaci6n de la interrupci6n SIGINT (CTRL +C) *1
void manipular _Ctrl_C( )
(
1* Inhabilitar CTRL +C *1
signal(SIGINT, SIG~GN);
1* Evitar hacer EIS en la rutina *1
escribir(''(.Abortar el proceso? sin ");
car = leer();
escribir(<C r  n ");
if (tolower(car) - - 's')
abort( );
else
I * La interrupci6n CTRL +C debe ser restaurada para nuestro
* manipulador, ya que por deJecto serla restaurada al
* manipulador del sistema.
*1
signal(SIGINT, manipular _Ctrl_C);
}
1* Escribe una cadena uti/izando //amadas al sistema *1
void escribir(char ~tr)
[
union REGS inregs, outregs;
inregs.h.ah = OxOe;
while (~tr)
[
inregs.h.al = ~tr+ +;
int86(OxIO,&inregs, &outregs);
}
}
I * Lee un cardcter uti/izando //amadas al sistema *1
int leer( )
[
return (_bios_keybrd(~EYBRD--.READ) & OxJf);
}
Esta fundon carga y ejecuta un nuevo proceso hijo. El proceso padre es
reemplazado en memoria por el proceso hijo, el cual es ejecutado inmedia-
tamente.
int execl(path, argO,argl, argn, NULL);
int execle(path, argO,argl, argn, NULL, envp);
int execlp(path, argO,argl, argn, NULL);
int execlpe(path, argO, argl, ... argn, NULL, envp);
int execv(path, argv);
int execve(path, argv, envp);
int execvp(path, argv);
int execvpe(path, argv, envp);
char *path;
char *argO,*argl,... *argn;
char *argv[ ];
char *envp[ ];
nombre del proceso a ser ejecutado
lista de punteros a argumentos
array de punteros a argumentos
array de punteros a variables del
entorno del proceso.
Existen varias versiones de execxxx( ), aunque todas ellas utilizan la
misma funci6n exec( ). El sufijo xxx que se afiade indica que argumentos
se pasan y c6mo se pasan. A continuaci6n se indican los sufijos y la varia-
ci6n que estos producen.
Los argumentos en la linea de 6rdenes son pasados a la fun-
ci6n exec( ) individualmente. Se utiliza normalmente en los
casos en los que el numero de argumentos es conocido.
Los argumentos en la linea de 6rdenes son pasados ala fun-
ci6n exec( ) como un array de punteros (...argv[ J). Se utiliza
normalmente en los casos en los que el numero de argumen-
tos es variable.
Pasa explicitamente un array de punteros (*envp[ J) a las va-
riables del entorno del proceso hijo.
Cada elemento del array envp, excepto el elemento final, apun-
ta a una cadena de la forma:
donde VAR es el nombre de la variable y valor es la cadena
de caracteres asociada.
char *mi_en vp[ J
{
"VARl=abcde':
"VAR2=xxx':
NULL
};
La funci6n exec() devuelve un valor -1 si ocurre un error y la variable
errno es puesta al valor correspondiente.
Los ficheros que estan abiertos cuando se ejecuta una Hamada a la
funci6n exec( ) permanecen abiertos en e1nuevo proceso. La funci6n exec( )
no conserva el modo de traslaci6n de los ficheros abiertos; si es necesario
utilizar la funci6n setmode( ). EI proceso hijo hereda el cntorno del proce-
so padre. Este entorno puede ser modificado por medio de la variable envp.
# include <stdio.h >
# include <conio.h >
# include <process.h>
char *mi_ent[ J =
{
"UNO=variable 1':
"DOS = variable 2':
'TRES=variable 3':
NULL
};
int main(void)
(
char *'1rgs[4],prog[80];
int car;
printj("Nombre del programa a ejecutar: ");
gets(prog);
printj("  n 1. execl
printj(" 5. execv
do
{
printf("  nEscribe un mimero entre 1 y 8 (0 para sa/ir): ");
car = getche();
if (car- = = '0') exit(O);
}
while ((car < '0') II (car> '8'));
printj("  n  n ");
2. execle
6. execve
4. execlpe  n' ');
8. execvpe  n");
3. execlp
7. execvp
/ * Argumentos para execvxx */
args[O] = prog;
args[1] = "exec??";
args[2] = "dos";
args[3] = NULL;
switch (car)
(
case '1':
execl(prog, prog, "execl': "dos': NULL);
break;
case '2':
execle(prog, prog, "execle': "dos': NULL, mi_ent);
break;
case '3':
execlp(prog, prog, "execlp': "dos': NULL);
break;
case '4':
execlpe(prog, prog, Hexeclpe': Hdos': NULL, mi_ent);
break;
case '5':
execv(prog, args);
break;
case '6':
execve(prog, args, mi_entj;
break;
case 7':
execvp(prog, args);
break;
case '8':
execvpe(prog, args, mi_ent);
break;
l
printf(H  nProceso no ejecutado.  n ");
exit(l);
J
Esta funci6n crea y ejecuta un nuevo proceso hijo. La diferencia con exec()
viene dada par el argumento modo; este determina la acci6n que toma el
proceso padre, antes de y durante la ejecuci6n del proceso hijo. Los sufijos
expuestos para exec( ) son v<ilidos tambien para spawn( ).
int spawnl(modo, path, argO, argl, argn, NULL);
int spawnle(modo, path, argO,argl, argn, NULL, envp);
int spawnlp(modo, path, argO,argl, argn, "NULL);
int spawnlpe(modo, path, argO,argl, argn, NULL, envp);
int spawnv(modo, path, argv);
int spawnve(modo, path, argv, envp);
int spawnvp(modo, path, argv);
int spawnvpe(modo, path, argv. envp);
iot modo;
char *path~·
char *argO,*argl,... *argn;
char *argv[ ];
char *envp[ ];
accion tomada por el proceso padre
nombre del proceso a ser ejecutado
lista de punteros a argumentos
array de punteros a argumentos
array de punteros a variables del
entorno del proceso.
Tiene el mismo efecto que las llamadas efectuactas
con la funcion exec( ) (destruye el proceso padre).
Suspende el proceso padre hasta que finalice la eje-
cucion del proceso hijo (spawn sincrono).
Continua la ejecucion del proceso padre concurren-
temente con el proceso hijo (spawn asincrono; va-
lido solamente en modo protegido).
Continua la ejecucion del proceso padre e ignora
las llamadas a wait( ) 0 cwait( ) contra los proce-
sos hijo (spawn asincrono; valida solamente en
modo protegido).
Continua la ejecucion del proceso padre concurren-
temente con el proceso hijo. El proceso hijo se des-
liga de la jerarquia de procesos padre; si el proceso
padre termina, el proceso hijo continua ejecutan-
dose (spawn asincrono; valida solamente en modo
protegido) .
El valor devuelto por la funcion spawn ( ) sincrona (P_ WAIT) es el
estado de salida del proceso hijo.
EI valor devuelto por la funcion spawn( ) asincrona (P_NOWAIT,
P~OWAITO) es el PID (identificacion del proceso). Para obtener el co-
digo de salida para un proceso Hamado con el modo P_NOWAIT se debe
Hamar a la funci6n wait( ) 0 cwait( ) y especificar ID. El c6digo de salida
para un proceso Hamado con el modo P_NOWAITO no puede obtenerse.
Si el proceso termina normalmente el c6digo de salida es O. Un valor
positivo indica una terminaci6n anormal. Si ocurre un error el valor de-
vuelto es -1 y la variable errno es puesta al valor correspondiente (el proce-
so hijo no comienza). El c6digo de salida puede ser modificado si el proce-
so hijo invoca explicitamente a la funci6n exit( ) con un valor diferente.
Los ficheros que estan abiertos cuando se ejecuta una Hamada a la
funci6n spawn(), permanecen abiertos en el nuevo proceso. El proceso hijo
hereda el entorno del proceso padre. Este entorno puede ser modificado
por medio de la variable envp.
La funci6n spawn( ) pasa al proceso hijo informaci6n de los ficherQs
abiertos, incluyendo el modo de traslaci6n, por medio de la variable de en-
torno C---.FILE~NFO (_C---.FILE~NFO en modo protegido). Esta va-
riable es utilizada por el c6digo de arranque de C (crtO.asm) para actuali-
zar el PSP del proceso hijo antes de que la funci6n main( ) asuma el control.
La variable -fileinjo determina si la informaci6n _C---.FILE~NFO es
o no pasada. Si vale 0, esta informaci6n no es pasada y si vale 1 sf es pasa-
da. La variable -fileinjo est a definida en stdlib.h y por defecto vale O. Si
se desea que tenga el valor 1 hay que definirla explicitamente en el progra-
ma C.
Para cualquier stream previa a una Hamada a una funci6n spawn( ),
debe ejecutarse explicitamente jjlush( ), jjlushall( ) 0 closer ).
# include <stdio.h >
# include < conio.h >
# include <process.h>
char *mi_en t[ J =
{
HUNO=variable 1':
HDOS=variable 2':
<TRES=variable 3':
NULL
};
int main(void)
(
char *'lrgs[4],prog[80];
int car, r;
printj(HNombre del programa a ejecutar: ");
gets(prog);
printj(H  n 1. spawnl 2. spawnle 3. spawnlp 4. spawnlpe  n ");
printj(H 5. spawnv 6. spawnve 7. spawnvp 8. spawnvpe  n");
do
{
printj(H nEscribe un mimero entre 1 y 8 (0 para saUr): ");
car = getche( );
if (car = = <0')exit(O);
}
while ((car < <0') II (car > <8'));
printj(H  n  n ");
/ * Argumentos para spawnvxx */
args[O] = prog;
args[l] = "spawn??";
args[2] = Hdos";
args[3] = NULL;
switch (car)
(
case <1':
r=spawnl(p _WAIT, prog, prog, "spawn/': "dos': NULL);
break;
case <2':
r=spawnle(p _ WAIT,prog,prog, "spawnle': "dos':NULL,mi_ent);
break;
case <3':
r=spawnlp(p _WAIT, prog, prog, "spawnlp': "dos': NULL);
break;
case '4':
r=spawn/pe(p _ WAIT,prog,prog,"spawnlpe':' 'dos' :NULL,mi_ent);
break;
case '5':
r=spawnv(p _ WAIT, prog, args);
break;
case '6':
r=spawnve(p _WAIT, prog, args, mi_ent);
break;
case 7':
r=spawnvp(p _ WAIT, prog, args);
break;
case '8':
r=spawnvpe(p _WAIT, prog, args, mi_ent);
break;
1
if (r = = -1)
printj(H  nProceso no ejecutado.  n");
exit(r);
Esta funci6n devuelve un puntero a la entrada de la tabla que contie-
ne la variable 0 un valor NULL si la variable no esta definida.
Esta funci6n anade, borra 0 modifica una variable de la lista de variables
del entorno.
Esta fundon devuelve un 0 si se ejecuta satisfactoriamente 0 un -1 si
ocurre un error.
PARTE
6
Graficos
• Gnificos con C
• Representaciones Gnificas
Para poder ejecutar los ejemplos gnificos mostrados en este capitulo es ne-
cesario tener un ordenador personal con una tarjeta gnifica CGA (Color
Graphics Adapter), EGA (Enhanced Graphics Adapter), VGA (Video Grap-
hics Adapter) 0 HGC (Hercules Graphics Card), entre otras. Tambien se
necesita un monitor, monocromo 0 color, que soporte gnificos basados en
puntos (pixels).
El monitor del ordenador personal consta generalmente de 25 lineas
de 80 caracteres, dependiendo estos datos de la interface de video instala-
da y del modo de video seleccionado. La linea superior es la fila 1, y la
posicion mas a la izquierda dentro de una fila, es la columna 1.
La unidad elemental en un dibujo no es una celda (posici6n que ocu-
pa un canicter), sino un punta en la pantalla. El numero exacto de puntos
sobre la pantalla depende del hardware que se tiene instalado y de la mo-
dalidad de video seleccionada (funci6n -.Setvideomode( ). Las coordena-
das de la esquina superior izquierda de la pantalla son (0,0). Por ejemplo,
el modo de video _ VRES16COLOR (VGA 16 colores), tiene una resolu-
ci6n de 640 x 480, 10 que significa que el eje x contiene los valores 0 a 639
y e1 eje y contiene los valores 0 a 479 (ver figura).
A la hora de desarrollar un programa grafico se deben tener en cuenta los
siguientes cinco puntos:
5. Volver a la configuraci6n de video inicial antes de salir del
programa.
# include <stdio.h>
# include <conio.h >
# include <graph.h>
int ModalidadDeVideo(void);
struct videoconfig cv;
/ *funci6n prototipo */
/ * datos referentes a la configuraci6n */
main( )
{
/ * Seleccionar la modalidad de video */
if (!ModalidadDeVideo( ))
{
printf(H%s  n': Hmodalidad de video no soportada");
exit(O);
l
/ * Determinar los parametros de la configuraci6n de video
* seleccionada y almacenarlos en cv.
*/
~etvideoconfig(&cv );
xm (cv.numxpixels/2-I);
ym = (cv.numypixels/2-I);
/ * centro eje x */
/ * centro eje y d
_rectangle( _GBORDER, xm-80, ym-50, xm+80, ym+50);
_ellipse( _GFILLINTERIOR, xm-70, ym-40, xm + 70, ym +40);
/ * Pulsar una tecla para continuar */
getch( );
/ * Restaurar la configuraci6n inicial para salir
* del program a
*/
----setvideomode( ----.DEFAULTMO DE);
1
/ * Sefeccionar fa modalidad de video */
int ModalidadDeVideo(void)
{
if (----Setvideomode(~ERCMONO))
return (~ERCMONO);
if (----setvideomode(_ VRES16COLOR))
return (_ VRES16COLOR);
if (----setvideomode(--.ERESCOLOR))
return(--.ERESCOLOR);
if (----setvideomode(-MRES4COLOR))
return (-MRES4COLOR);
else
return (0);
Este programa dibuja un rectangulo y una elipse inscrita en el rec-
tangulo.
Las constantes listadas a continuaci6n son utilizadas para activar la moda-
lidad de video. La modalidad de video elegida debe ser compatible can el
hardware instalado en la maquina.
Constante Caracteristicas Modo Adaptador
_DEFAULTMODE volver al modo original ambos todos
_TEXTBW40 40 cols. texto, 16 grises texto CGA
_TEXTC40 40 cols. texto, 16/8 colores texto CGA
_TEXTBW80 80 coIs. texto, 16 grises texto CGA
_TEXTC80 80 cols. texto, 16/8 colores texto CGA
~RES4COLOR 320 x 200 pixels, 4 colores gnificos todos
_MRESNOCOLOR 320 x 200 pixels, 4 grises gnificos CGA
_HRESBW 640 x 200 pixels, BW gnificos CGA
Constante Caracteristicas Modo Adaptador
_TEXTMONO 80 eols. texto, BW texto MDPA
_HERCMONO 720 x 348 pixels, BW para HGC gnifieos HGC
_MRESI6COLOR 320 x 200 pixels, 16 eolores gnifieos EGA
_HRESI6COLOR 640 x 200 pixels, 16 eolores grafieos EGA
_ERESNOCOLOR 640 x 350 pixels, BW gnifieos EGA
_ERESCOLOR 640 x 350 pixels, 4 0 16 eolores gnificos EGA
_ VRES2COLOR 640 x 480 pixels, BW gnifieos VGA
_ VRES16COLOR 640 x 480 pixels, 16 eolores gnifieos VGA
_MRES256COLOR 320 x 200 pixels, 256 eolores grafieos VGA
_ORESCOLOR 640 x 400 pixels, 1 0 16 eolores (Olivetti) grafs.
Para utilizar el modo _HERCMONO hay que instalar previamente
el programa residente MSHERC.COM, suministrado con el paquete Mi-
crosoft C.
Yaque algunos adaptadores gnificos soportan varios modos de video,
Microsoft C provee dos modos mas:
Selecciona el modo de video de mas alta resolu-
ci6n de entre todos los soportados por nuestro
hardware.
_MAXCOLORMODE Selecciona el modo de video que soporte mas co-
lores de entre todos los soportados por nuestro
hardware.
Para seleccionar una modalidad de video de las anteriores, disponemos de
la funci6n:
Antes de salir del programa gnifico, debemos restaurar la modalidad de
video original con la funci6n:
Esta funci6n almacena valores y series de caracteres con formato en una
memoria intermedia (buffer).
iot spriotf(bujjer, jormato[, arg}...);
char *bujjer;
coost char *jormato;
Los argumentos jormato y arg tienen el mismo significado que en la
funci6n printj( ).
La funci6n sprintj( ) devuelve el numero de caracteres almacenados
en bujjer, sin incluir el canicter de terminaci6n  O.
iot c;
char bujjer[80};
main( )
(
char *n = "Francisco Javier";
char *0 =.."Ceballos";
c = sprintj(bujjer, "Nombre: %s': n);
c + = sprintj(bujjer+c, " %s  n': a);
printj("%s': bujjer);
1
En este ejemplo c toma el valor del numero de caracteres actualmente
almacenados en buffer. La expresi6n buffer+c incrementa el puntero buf-
fer con el fin de afiadir la siguiente informaci6n a continuaci6n de la actual.
Los panimetros de la configuraci6n seleccionada se almacenan en una es-
tructura de tipo videoconfig, con el fin de utilizarlos con otras funciones
graficas y para asegurar la portabilidad a otras configuraciones (CGA, EGA
o VGA). Esta operaci6n se realiza mediante la funci6n -f5etvideoconfig( ).
struct videoconfig cv;
-f5etv ideoconjig( &cv);
y = cv.numxpixe/s / 2 - 1; / * centro eje x */
x cv.numypixe/s / 2 - 1; / * centro eje y */
EI siguiente programa puede ser de gran utilidad, ya que presenta en
pantalla las configuraciones de video que acepta nuestro ordenador. Cad a
vez que se presente una configuraci6n pulse una tecla para ver la siguiente.
# include <conio.h >
# include <stdio.h>
# include <graph.h>
short modos[ ] = ! _TEXTBW40, _TEXTC40, _TEXTBW80,
_TEXTC80, --.MRES4COLOR, --.MRESNOCOLOR,
JlRESBW, _TEXTMONO, JlERCMONO,
--.MRES/6COLOR,JlRES/6COLOR, JRESNOCOLOR,
JRESCOWR, _VRES2COLOR, _VRES/6COLOR,
--.MRES256COLO~ORESCOLOR
};
char *nombres[} = [ "TEXTBW40'; "TEXTC40'; "TEXTBW80';
"TEXTC80'; "MRES4COLOR'; "MRESNOCOLOR';
"HRESBW'; "TEXTMONO'; "HERCMONO';
"MRES16COLOR'; "HRES16COLOR'; "ERESNOCOLOR';
"ERESCOLOR'; "VRES2COLOR'; "VRES16COLOR';
"MRES256COLOR'; "ORESCOLOR"
];
/ * Posible ntimero de filas */
short filas[ } = [ 60, 50, 43, 30, 25 ];
main( )
[
short c, i, j, x, y, nfilas;
short num = sizeof(modos) / sizeof(modos[O});
struct videoconfig cv; / * conjiguraci6n de video */
char b[500}; / * buffer para la funci6n sprint! */
/ * Poner a prueba cada modo */
for (i = 0; i < = num; i+ +)
{
for (j = 0; j < 5; j + +)
[
/ * Probar cada ntimero posible de filas */
nfilas = ---.Setvideomoderows(modos[i], filas[j});
if ((/nfilas) II (filas[j} /= nfilas))
continue;
else
[
/ * obtener la configuraci6n de cv */
~etvideoconfig( &cv);
y = (cv.numtextrows - 14) / 2;
x = (cv.numtextcols - 31) / 2;
/ * Elegir una ventana para sacar el texto */
---.Settextwindow(y, x, cv.numtextrows-y, cv.numtextcols-x);
CAPITULO 21: GRAFICOS CON C 733
* para despues visualizar el contenido del buffer
*/
c = sprintj(b, "Modalidad de video: %sn':
nombres[iJ);
c += sprintj(b + c, "Puntos eje X' O/Odn':
cv.numxpixels};
c += sprintj(b + c, "Puntos eje }; O/Odn':
cv.numypixels};
c += sprintf(b + c, "Columnas de texto: O/Odn':
cv.numtextcols};
c += sprintj(b + c, "Fi/as de texto: O/Odn':
cv.numtextrows};
c += sprintj(b + c, "Colores: O/Odn':
cv.numcolors};
c + = sprintj(b + c, "Bits/pun to: %d n':
cv.bitsperpixel};
c += sprintj(b + c, "Pdginas de video %d  n':
cv.numvideopages};
c += sprintj(b + c, "Modo: O/Odn':
cv.mode};
c += sprintf(b + c, ''Adaptador: O/Odn':
cv.adapter};
c += sprintf(b + c, "Monitor: O/Odn':
cv.monitor};
c += sprintf(b + c, "Memoria: O/Od':
cv.memory};
c + = sprintj(b + c, " n  nPulse una tecla para continuar"};
/ * Visualizar el contenido del buffer */
_outtext(b};
getch( }; / *pulsar una tecla para continuar */
J
J
J
_displaycursor( _GCURSORON};
---.Setvideomode(----.DEFAULTMO DE);
J
/ * cursor visible d
/ * volver al modo inicial */
Existen dos modalidades de video para texto en color: _ TEXTC40 y
_ TEXTC80, las cuales pueden ser utilizadas con los adaptadores CGA,
MCGA, EGA y VGA. Estos modos proporcionan 16 colores para el pri-
mer plano y 8 colores de fondo.
Cada canlcter requiere dos bytes de la memoria de video. El primero
contiene el c6digo ASCII del canicter y el segundo los atributos de presen-
taci6n.
La tabla siguiente muestra los colores para el texto y sus valores aso-
ciados (color del primer plano):
Nro. Color Constante Nro. Color Constante
0 Negro _BLACK 8 Gris _GRAY
1 Azul _BLUE 9 Azul claro _LIGHTBLUE
2 Verde GREEN 10 Verde claro _LIGHTGREEN
3 Cyan CYAN 11 Cyan claro _LIGHTCYAN
4 Raja RED 12 Raja claro LIGHTRED
5 Magenta _MAGENTA 13 Magenta claro _LIGHTMAGENTA
6 Marron BROWN 14 Amarillo _LIGHTYELLOW
7 Blanco WHITE 15 Blanco intenso _BRIGHTWHITE
Para el fondo es valida cualquier color del 0 al 7. Para seleccionar el
color de fonda disponemos de la funci6n:
Para escribir un texto en un color determinado, seleccionar primero
el color ( --..Settextcolor(constante) ) y a continuaci6n visualizar el texto
( _outtext(buffer) ).
# include <stdio.h>
# include <graph.h >
struct videoconfig cv;
char *nombre[ } = ("NEGRO'; "AZUL'; "VERDE'; "CYAN';
"ROJO'; "MAGENTA'; "MARRON'; "BLANCO" J;
main( )
{
short cjondo;
short ctexto;
char buffer[80};
/ * color de jondo de 0 a 7 */
/ * color del texto 0 a 15 d
/ * almacenar cada linea a visualizar d
-setvideomode(_TEXTC80); / * establecer la modalidad de video */
-ftetvideoconjig(&cv); / * conjiguraci6n de video */
for (cjondo = 0; cfondo < 8; cjondo+ +) / * color de jondo */
(
-setbkcolor(cjondo);
-settextposition(l, 1);
print/("Color de jondo: %7s  n'; nombre[cjondo]);
for (ctexto = 0; ctexto < = 16; ctexto + +) / * color texto */
(
-settextcolor( ctexto);
-settextposition(5 +ctexto, 35); / * posicionar el cursor */
sprintf(bujjer, "Color: %2d  n'; ctexto);
_outtext(bujjer); / * visualizar el texto coloreado d
}
/ *pulse una tecla para continuar */
getch( );
}
_clear screen(_GCLEARSCREEN);
-setvideomode( -DEFAULTMODE);
}
/ * limpiar la pantalla */
/ * volver al modo original */
Este programa presenta para cada color de fonda (0 a 7) los 16 colores
posibles en los que se puede presentar un texto.
Si al valor del color del primer plano Ie sumamos 16, se obtienen ca-
racteres parpadeando; esto es, los valores 16 a 31 son los mismos colores
o a 15, pero parpadeando.
blanco sobre negro
blanco intenso sobre negro
blanco intermitente sobre negro
blanco intenso e intermitente sobre negro
negro sobre blanco
amarillo sobre azul
El color del primer plano puede coincidir con el color de fondo, 10
cual hace invisible cualquier canicter.
Existen dos modalidades de video para gnificos en color utilizando un adap-
tador CGA: ~RES4COWR y ~RESNOCOWR. Con CGA en alta
resolucion,_HRESBW, solo es posible trabajar en blanco y negro.
En modo gnifico un pixel es representado por un bit (blanco y negro),
dos bits (4 colores), cuatro bits (16 colores), u ocho bits (256 colores), de-
pendiendo del modo seleccionado.
Con la modalidad de video ~RES4COWR, se dispone de cuatro
paletas de cuatro colores cada una. Cada color tiene asociado un valor or-
dinal de 0 al 3. El color 0 es el color de fondo, 10 que producini una salida
invisible y los colores dell al 3, son tres colores de los 16 posibles.
EI color de fondo puede tomar cualquier valor de 0 a 15y para selec-
cionarlo disponemos de la funci6n:
Verde
Rojo
Marr6n
Cyan
Magenta
Oris claro
Verde claro
Rojo claro
Amarillo
Cyan claro
Magenta claro
Blanco
Utilizando la modalidad de video ~RESNOCOLOR con un moni-
tor blanco y negro se producen distintas tonalidades de grises. Si se utiliza
con un monitor de color, se dispone de las dos paletas siguientes:
Azul
Rojo
Oris claro
Azul claro
Rojo claro
Blanco
# include <stdio.h>
# include <conio.h >
# include <graph.h >
long color-fondo[8] = (-BLACK, -BLUE, _GREEN, _CYAN,
---.RED,---.MAGENTA,-BROWN, _WHITE};
char *nombre[ ] = ("NEGRO': "AZUL': "VERDE': "CYAN':
"Raja': "MAGENTA': "MARRON'; "BLANCO"};
main( )
(
int cfondo;
int paleta;
int color;
/ * color de fondo de 0 a 7 */
/* paleta de"O a 3 */
/ * color 0 a 3 de la paleta elegida */
---.Setvideomode(--.MRES4COLOR); / * modalidad de video */
---ftetvideoconfig(&cv); / * configuraci6n de video */
for (cfondo = 0; cfondo < 8; cfondo+ +) /* color de fondo */
(
---.Setbkcolor(color-fondo[cfondo]);
for (paleta = 0; paleta < 4; paleta+ +) / * paleta elegida */
(
---.Selectpalette(paleta);
for (color = 0; color < 4; color+ +) / *primer plano */
(
---.Settextposition(l, 1);
---.Setcolor(color);
printj("Color de fondo: % 7s  n': nombre[cfondo]);
printj("Paleta: %16d nColor: %17d n': paleta, color);
---fectangle(_GFILLINTERIOR, 160, 100, 320, 200);
/ * pulse una tecta para continuar */
getch( );
}
}
}
---.Setvideomode(-DEFAULTMODE);
}
Este programa presenta todas las combinaciones de colores de fonda
con las pal etas 0 a 3.
Los colores de video estan producidos par combinaciones de cuatro ele-
mentos (4 bits): tres componentes de color, (rojo, verde y azul) mas un com-
ponente de intensidad. El resultado son 16 combinaciones de color.
Los datos del buffer de video constan de valores de atributos de 4 bits.
En el CGA, cada uno de estos valores se corresponde con uno de los 16
posibles colores. En el EGA, cad a valor de atributo design a un registro de
los 16 posibles de la paleta, cad a uno de los cuales contiene un valor de
color. Cada registro puede contener un color de 64 diferentes (se emplean
6 bits de color). En el MCGA, se utiliza un componente similar a la t>aleta
del EGA, el DAC de video (convertidor digitallanal6gico de video), el cual
contiene 256 registros de color. Cada registro es de 32 bits distribuidos de
la forma siguiente:
El byte mas significativo contiene ceros. Los siguientes, contienen el
nivel de intensidad (0 a 63) de azul (A), verde (V) y rojo (R). Con un valor
de atributo de 4 bits s610 se pueden utilizar 16 registros. Para hacer uso
de los 256 registros se necesita utilizar un modo de video que utilice atri-
butos de 8 bits. En el VGA, se utilizan 16 paletas, como la del EGA, y el
DAC de video como en el MCGA. De este modo, un valor de atributo de
4 bits selecciona un registro de la paleta activa, cuyo valor selecciona a su
vez uno de los 256 registros de color del DAC de video, cuyo contenido
determina el color.
Trabajar con un EGA, MCGA 0 VGA es, 16gicamente, mas complica-
do que hacerlo con un CGA. Por ello, inicialmente el sistema carga los re-
gistros de la paleta con los valores de color que coinciden con los colores
disponibles en el CGA. De esta forma, utilizando modos de video compa-
tibles con el CGA, veremos los mismos colores que obtendriamos con un
CGA. Para cambiar la paleta y/o los colores utilizar las funciones rema-
pallpalette( ) y remappalette( ).
Las funciones graJicas necesitan informacion acerca de la posicion (coor-
denadas x,y) donde se quiere dibujar. Estas coordenadas se pueden expre-
sar de dos formas:
El sistema de coordenadas fisicas es el establecido por omision. Tiene su
origen en e1punta (0, 0). Los valores en este sistema son siempre positivos.
El valor de x aumenta de izquierda a derecha y el valor de y de arriba a
abajo. Los valores de x e y se expresan en puntos (pixels).
Un sistema de coordenadas logicas es creado al mover el origen a una posi-
cion determinada utilizando la funcion ~etvieworg( ). A partir de este
instante nuestro origen (0, 0) estanl en la posicion indicada por esta fun-
cion, por 10 que el resto de las funciones gnificas referinin los valores de
coordenadas empleados, a este punto. Los valores de x e y, mantienen su
orientacion.
# include <stdio.h>
# include <conio.h >
# include <graph.h >
main( )
(
/ * Seleccionar la modalidad de video */
--.:setvideomode( --.MAXRESMODE);
/ * Determinar los parametros de la conjiguraci6n de video
* seleccionada y almacenarlos en cv.
*/
~etvideoconjig(&cv);
xm cv.numxpixels/2-1;
ym = cv.numypixels/2-1;
/ * centro eje x */
/ * centro eje y */
/ * Establecer un sistema de coordenadas 16gicas */
-----setvieworg(xm,ym);
/ * Pulsar una tecla para continuar */
getch( );
/ * Restaurar la conjiguraci6n inicial */
-----setvideomode(---.DEFAULTMODE);
}
Este program a establece el origen de coordenadas (0, 0) en el centro
de la pantalla y dibuja un cuadrilatero centrado en la misma.
Para pasar de coordenadas fisicas a 16gicas, disponemos de la funci6n
~etviewcoord(x, y); y para pasar de coordenadas 16gicasa fisicas utilizar
la funci6n ---I?etphyscoord(x, y). Los resultados son devueltos en la estruc-
tura xycoord.
Para utilizar las funciones graficas pensemos primero si hemos incluido
la libreria gnlfica en el modelo de memoria que estemos utilizando. De no
ser asi, realizar el enlace utilizando explicitamente esta libreria. Las decla-
raciones para estas funciones estan en el fichero graph.h. Las podemos agru-
par en funci6n de la tarea que desempefian, asi:
_GCURSORON
_GCURSOROFF
cursor visible
cursor no visible.
Cuando se ejecuta un programa grafico, el cursor, por defecto, es visi-
ble en modo texto y no visible en modo grafico.
Selecciona la modalidad de video apropiada para la interface de video ins-
talada en e1ordenador. El argumento modo es una constante de las especi-
ficadas en la tabla expuesta en este mismo capitulo.
Esta fund6n devuelve un valor distinto de 0 si la modalidad de video
elegida es soportada, en caso contrario el valor devuelto es O.
Esta funci6n devuelve el numero de filas actualmente puestas. Un va-
lor 0 significa que el modo de video no es soportado. Puede suceder que
e1valor Ii/as no sea soportado, 10 cual no significa un error.
Almacena en una estructura de tipo videoconfig los parametros de la con-
figuraci6n de video elegida.
struct videoconfig ~ar * _far _getvideoconfig(struct videoconfig
~ar *cv);
struct videoconjig cv;
-ltetvideoconjig( &cv);
Para configuraciones que soportan multiples pagmas de video
---.Setactivepage( ) espedfica el area de memoria donde son almacenados
los resultados graficos procedentes de la ejecucion del programa. El argu-
mento pag selecciona la pagina activa en un instante determinado. Por de-
fecto es la pagina O.
Esta fundon devuelve el numero de pagina anteriormente activa. Si
ocurre un 'error la funcion devuelve un valor negativo.
Con COA solamente se dispone de 16Kde RAM para soportar multi-
ples paginas de video y solamente en modo texto. Los adaptadores EOA
y VOA pueden soportar hasta 256K de RAM para multiples paginas de
video en modo grafico.
Para configuraciones que soportan multiples pagmas de video
---.Setvisualpage() selecdona la pagina de video a visualizar. Mientras tan-
to el programa puede continuar e ir almacenando resultados graficos en
otra pagina activa (ver fundon ---.Setactivepage(). El argumento pag es-
pecifica la pagina a visualizar. Por defecto es la pagina O.
Esta fundon devuelve el numero de pagina anteriormente visualiza-
da. Si ocurre un error la funcion devuelve un valor negativo.
# include <stdio.h>
# include <graph.h >
# include <conio.h >
main( )
{
int p = 0;
while (!kbhit( )) / * repetir hasta pulsar una tecla */
(
/ * alternar entre la pagina 0 y la 1 */
--setactivepage(p & 1);
--setcolor(p % 16);
-'ectangle(_GFILLINTERIOR, 90, 60, 230, 140);
--setvi'sualpage(p + + & 1);
1
--setvideomode( --.DEFAULTMODE);
}
Este programa activa la pagina 0 6 1 y visualiza la 1 6 0 hasta pulsar
una tecla.
struct xycoord
{
short xcoord; coordenada x
short ycoord; coordenada y
} ~ar _setvieworg(short x, short y); nuevo origen
Esta funcion devuelve en una estructura de tipo xycoord las coorde-
nadas fisicas del origen logico anterior.
struct xycoord
(
short xcoord;
short ycoord;
) -3ar _getviewcoord(short x, short y);
coordenada x
coordenada y
coordenadas /fsicas
Esta funcion devuelve en una estructura de tipo xycoord las coorde-
nadas logicas resultantes.
struct xycoord
(
short xcoord; coordenada x
short ycoord; coordenada y
) -3ar _getphyscoord(short x, short y); coordenadas /6gicas
Esta funcion devuelve en una estructura de tipo xycoord las coorde-
nadas fisicas resultantes.
Limita el area de visualizaci6n de la pantalla al rectangulo definido por
(xl, yl) y (x2, y2). El punta (xl, yl) corresponde ala esquina superior iz-
quierda del rectangulo y el punta (x2, y2) corresponde a la esquina inferior
derecha del rectangulo.
----.Setvideomode( ~RES4COLOR);
----.Setcliprgn(O, 0, 160, 100); / * area de visualizaci6n */
_ellipse(_GFILLINTERIOR, 110, 58, 210, 142);
Este ejemplo limita el area de visualizaci6n al cuadrante superior iz-
quierdo de la pantalla. La funci6n _ellipse( ) pinta un circulo centrado
en la pantalla, del cual s610 podra verse el cuadrante superior izquierdo.
Limita el area de visualizaci6n de la pantalla a un rectangulo definido por
(xl, yl), esquina superior izquierda, y por (x2, y2), esquina inferior dere-
cha y cambia al sistema de coordenadas 16gicas situando el origen en el
punta fisico (xl, yl).
----.Setvideomode( ~RES4COLOR);
----.Setviewport(160, 100, 319, 199);
Este ejemplo define como area de visualizaci6n el cuadrante inferior
derecho de la pantalla y situa el origen 16gico en el punta fisico (160, 100).
_remapallpalette(colores) y
_remappalette(color, color_nuevo)
Permiten cambiar los colores asignados alas paletas si el modo gnifico y
el hardware 10 soportan. Los adaptadores EGA y VGA disponen de la ca-
pacidad de asignar nuevos colores a una paleta. Mediante la funci6n
~emappalette( ) se puede cambiar un color y mediante la funci6n
~emapallpalette( ) se cambian todos los colores de una paleta.
colores es un array con los valores de los colores. El primer valor en
el array seria el nuevo color asociado con el color o.
La funci6n _remapallpalette( ) devuelve un valor distinto de 0 si se
ejecuta satisfactoriamente y un 0 en caso contrario. La funci6n
_Jemappalette( ) devuelve el color anterior 0 un -1 si ocurre un error.
Ejemplo:
# include <stdio.h >
# include <graph.h>
long colores[16J=(JLACK, JLUE, _GREEN, -RED, -RED, J1AGENTA,
JROWN, _WHITE, _GRAY, JIGHTBLUE, JIGHTGREEN,
JIGHTRED, JIGHTRED, JIGHTMAGENTA,
JIGHTYELLOW, JRIGHTWHITE
J;
main( )
I
----.Setvideomode(-MRES16COLOR);
~emapallpalette( colores);
----.Setcolor(3);
~ectangle(_GFILLINTERIOR, 110, 58, 210, 142);
getch( );
----.SetVideomode(---.DEFAULTMODE);
J
El array colores reasigna la paleta de colores por defecto de una EGA,
de tal forma que los colores cyan y light cyan son visualizados como red
y light red.
Esta funcion trabaja solamente bajo las modalidades MRES4COLOR,
MRESNOCOLOR y ORESCOLOR, permitiendo elegir una paleta de las
definidas. El numero de paleta a elegir viene dado por el argumento num.
La funcion -selectpalette() devuelve el numero de la pal eta anterior-
mente elegida 0 un -1 si ocurre un error.
-setvideomode( --.MRES4COLOR);
----.Selectpalette(2);
----.Setcolor(I);
_..5etviewport(l60, 100, 319, 199);
_ellipse(_GFILLINTERIOR, -50, -42, 50, 42);
/ * paleta elegida */
/ * color 1 de la paleta 2 */
Este ejemplo visualiza el cuadrante inferior derecho de un circulo, en
color verde claro, correspondiente a la paleta 2.
En modo grcifico el color de fonda debe especificarse par media de
la constante correspondiente.
Devuelve como resultado el color de fonda actual. Par defecto este valor
es O.
Pone como color del primer plano el indicado par el argumento color. Par
defecto este valor es el valor mas alto de la paleta can la que estemos tra-
bajando.
La funcion ---setcolor( ) devuelve como resultado el color previa a un
-1 si ocurre un error.
Devuelve como resultado el color actual del primer plano. Par defecto este
valor es el valor mas alto de la paleta can la que estemos trabajando.
Ejemplo:
c = ---$etcolor( );
Fija el tipo de linea a dibujar par otras funciones como _lineto( ) y
-l'ectangle( ). El tipo de linea es fijado par una mascara de 16bits. Cada
bit representa un punta en la linea. Si un bit es 1 se dibuja un punta y si
es 0 no se dibuja. El argumento mascara es por defecto OxFFFF, 10 que
da lugar a una linea continua.
Este ejemplo fija la mascara 1100110011001100,10 que da lugar a li-
neas discontinuas.
Devuelve un numero correspondiente al valor actual de la mascara que se
esta utilizando para trazar lineas por otras funciones. La mascara es un
valor de 16bits donde un 1 significa dibujar un punta y un 0 no dibujarlo.
En terminos mas tecnicos diriamos: si el bit es 1 el punta correspondiente
se pone al color de la linea y si el bit es 0 el punta correspondiente se deja
como esta, no cambia. Por defecto el valor de la mascara es OxFFFF.
Este ejemplo almacena en la variable estilo el valor actual de la mas-
cara utilizada para el trazado de lineas.
Recubrir un area, de acuerdo con una mascara formada por un array de
8 por 8 bits, donde cada bit representa un punto. Si el bit es 1 el punta
correspondiente se pone al color actual y si el bit es 0 el punta correspon-
diente se deja como esta, no cambia. Par defecto el argumento mascara
es NULL.
unsignedchar *mascara= [ HxOOx3Fx30x30x3Cx30x30x30" J;
---setjillmask((char-far *)mascara);
Devuelve el valor actual de la mascara formada por un array de 8 par 8
bits, utilizada para recubrir areas. Ver tambien la funci6n ---setjillmask( ).
unsigned char *mascara= [ H  xOO x3F x30  x30  x3C  x30 x30  x30" J;
char *masc = "12345678"; / * inicializar el array */
~etjillmask(masc);
---setjillmask((char _far *)mascara);
-----setcolor(2);
--l"ectangle(_GFILLINTERIOR, 110, 57, 210, 142);
-----setjillmask(masc); / * restaurar mascara */
/ * salvar mascara actual */
/ * mascara nueva */
getch( ); / * Pulsar una tec/a para continuar */
-----setvideomode(---.DEFAULTMODE); / * configuraci6n inicial */
En la mascara, con el primer caracter se representa la primera linea,
con el segundo caracter la segunda linea y as! sucesivamente.
Para recubrir un area de acuerdo con una determinada mascara pri-
mero se construye esta de la forma siguiente:
Binario Hexadecimal Decimal Figura
00000000 00 0
00111111 3F 63
00110000 30 48
00110000 30 48
00111100 3C 60
00110000 30 48
00110000 30 48
00110000 30 48
10 cual se expresa de la forma:
Recubre un area utilizando el color y la mascara actuales. Los argumentos
x e y corresponden alas coordenadas de un punto. Si el punto esta dentro
de la figura se recubre su interior y si esta fuera se recubre el exterior. El
punto no debe estar sobre el borde de la figura. El argumento limite es una
expresi6n numeric a que identifica el color utilizado para pintar el borde
de la figura. El area a recubrir queda limitada por este borde.
La fundon --floodjill( ) devuelve un valor distinto de 0 si se ejecuta
satisfactoriamente 0 un 0 en caso contrario.
unsigned char mascara[2][8] = {{0,66,36,24,24,36,66,0 },
[ 0,24,0,102,102,0,24,0}};
int i;
char *masc = "12345678";
1* Seleccionar la modalidad de video *1
---.Setvideomode(-MAXRESMODE);
1* Dibujar un rectangulo y recubrirlo 2 veces *1
~etjillmask(masc); 1* salvar mascara actual *1
---.Setcolor(l);
--.rectangle(_GBORDER, 109, 56, 211, 143);
for (i = 0; i < 2; i+ +)
[
---.Setjillmask((char ~ar *)mascara[i]); I * mascara nueva *1
---.Setcolor(i + 2);
--floodjill(l60, 100, 1); 1* parar en el borde de color 1 *1
}
---.Setjillmask(masc); 1* restaurar mascara *1
Mueve la posicion actual de salida gnifica al punta de coordenadas (x, y).
No dibuja.
struct xycoord
[
short xcoord;
short ycoord;
J _far _moveto(short x, short y);
coordenada x
coordenada y
nueva posicion
La funcion _moveto( ) devuelve las coordenadas logicas de la posi-
cion anterior en una estructura de tipo xycoord.
Dibuja una linea desde la posicion actual hasta el punta (x, y). Cuando
se utiliza la funcion -floodfil/( ) para recubrir una figura el borde debe
ser una linea continua.
La fundon _lineto( ) devuelve un valor distinto de 0 si la operacion
se desarrolla satisfactoriamente 0 un 0 en caso contrario.
Dibuja un rectangulo. Los puntos (xl, yl) y (x2, y2) corresponden alas
esquinas superior izquierda e inferior derecha respectivamente. EI argumento
c es una de las constantes siguientes:
La funci6n _rectangle( ) devuelve un valor distinto de 0 si se ejecuta
satisfactoriamente 0 un 0 en caso contrario.
# include <stdio.h>
# include <conio.h >
# include <graph.h>
main( )
{
unsignedchar *mascara = {"  xFO xFO xFO xFO xOF xOF xOF xOF"];
char *masc = "12345678"; / * inicializar el array */
static int c[ ] = { 10,20, 50,20, 55,25, 55,40, 50,45, 10,45,
50,45, 55,50, 55,65, 50,70, 10,70, 10,20 },o
unsigned short estilo, n,o
short color, jx, jy,o
/ * Seleccionar la modalidad de video */
---.Setvideomode( -MAXRESMODE),o
---f5etvideoconjig(&cv),o / * almacenar conjiguraci6n */
/ * Factores de escala en junci6n de la resoluci6n */
fx = cv.numxpixels/320;
fy = cv.numypixels/200;
/ '"Dibujar una diagonal */
_moveto(O,O);
_lineto( cv.numxpixels-l, cv.numypixels-l);
/ * Dibujar una horizontal con formato */
estilo = ---I5etlinestyle( );
---setlinestyle(OxFOF);
_moveto(O, cv.numypixels/2);
_lineto(cv.numxpixels-l, cv.numypixels/2);
---setlinestyle( estilo);
/ * Dibujar rectangulo coloreado */
---I5etjillmask(masc);
---setjillmask((char far *)mascara);
color = ---I5etcolor(); ---setcolor(2);
-rectangle(_GFILLINTERIOR, 124x, 244y, 634x, 754y);
---setcolor(color); / * restaurar color */
---setjillmask(masc); / * restaurar mascara */
/ * salvar mascara actual */
/ * mascara nueva */
/ * Dibujar la letra B dentro del rectangulo */
_moveto(c[OJ4x, c[lJ4y);
for (n = 0; n < 24; n + = 2)
_lineto(c[nJ4x, c[n+l1*fy);
---settextposition(9, 94y);
printj(HIENVENIDO A Microsoft e");
getch( ); / * Pulsar una tecla para continuar */
---setvideomode(----.DEFAULTMODE); / * configuraci6n inicial */
J
Dibuja una elipse. El centro de la elipse es el centro del rectangulo defini-
do por los puntos (xl, yl) y (x2, y2). El argumento c es una de las constan-
tes siguientes:
La funci6n _ellipse( ) devuelve un valor distinto de 0 si se ejecuta sa-
tisfactoriamente 0 un 0 en caso contrario.
Dibuja un arco. El centro del arco es el centro del rectangulo definido por
los puntos (xl, yl) y (x2, y2). El arco comienza en el punta de intersecci6n
con el vector definido por (x3, y3) y finaliza en el punta de intersecci6n
con el vector definido por (x4, y4). El arco es dibujado en sentido contra-
rio alas agujas del reloj.
short _far _arc(short xl, short yl, short x2, short y2, short x3,
short y3, short x4, short y4);
La funci6n _arc( ) devuelve un valor distinto de 0 si se ejecuta satis-
factoriamente 0 un 0 en caso contrario.
Dibuja un area limitada por un arco y dos radios. El centro del arco es
el centro del rectangulo definido por los puntos (xl, yl) y (x2, y2) y los
radios van desde el centro del arco a los puntos (x3, y3) y (x4, y4) respecti-
vamente. El arco es dibujado en sentido contrario alas agujas del reloj.
El argumento c es una de las constantes siguientes:
short _far _pie(short c, short xl, short yl, short x2, short y2,
short x3, short y3, short x4, short y4);
La funci6n -pie( ) devue1ve un valor distinto de 0 si se ejecuta satis-
factoriamente 0 un 0 en caso contrario.
# include <stdio.h>
# include <conio.h >
# include <graph.h >
main( )
(
short x, y, color;
/ * Seleccionar la modalidad de video */
--.Setvideomode( --.MAXRESMODE);
---l:etvideoconjig(&cv); / * almacenar conjiguracion */
/ * Establecer coordenadas logicas */
x = cv.numxpixels/2 - 1;
y = cv.numypixels/2 - 1;
-setvieworg(x, y);
-selectpalette(3 );
color = ---l:etcolor( );
/ *paleta 3 */
/ * color actual *!
/ * Dibujar rectdngulo */
-,"ectangle(_GBORDER, -80, -50, 80, 50);
l* Dibujar sector */
-pie(_GBORDER, -60, -40, 60, 40, 0, -40, 70, 40);
-setcolor(1);
-floodjil/(-5, 0, color);
/ * color 1 */
/ * colorear sector */
/ * Colorear rectdngulo excepto sector */
-setcolor(2);
-floodjil/(-55, -35, color);
/ * Pulsar una tecla para continuar */
getch( );
~etvideomode( ---.DEFA ULTMODE);
J
Este ejemplo dibuja un rectangulo y en su interior un sector circular,
coloreando ambas figuras.
La funcion ~etpixel( ) devuelve las coordenadas del punta anterior-
mente dibujado. Si la funcion falla devuelve el valor -1.
La funcion ---I5etpixel() devuelve el color correspondiente al punta
(x, y). Si la funcion falla devuelve el valor -1.
Devuelve las caardenadas 16gicas de la posicion actual en una estructura
de tipo xycoord. Esta funcion no es valida para texto (ver funciones para
texto a continuacion).
struct xycoord
(
short xcoord;
short ycoord;
J _far _getcurrentposition( );
coordenada x
coordenada y
Fija el color para el texto. El argumento constante es un valor de 0 a 31.
Los valores 0 a 15 corresponden a los colores normales y los valores 16 a
31 corresponden a los mismos colores, pero hacen que el texto parpadee.
El color por defecto para el texto es el de valor mas alto.
Da como resultado el color correspondiente a la posicion actual del cursor
en el texto. Por defecto es el valor mas alto.
Situa el cursor en la fila y columna indicada por los argumentos fila y col.
Las salidas posteriores de texto producidas por la funcion _outtext( ) 0
por otras funciones seran colocadas a partir de ese punto.
short row;
short co);
} ~ar --Settextposition(short fila, short co!);
mlmero de fila
mimero de columna
La funcion ~ettextposition( ) devuelve la posicion anterior en una
estructura de tipo rccoord definida en graph.h.
Da como resultado la fila y columna de la posicion actual del cursor en
el texto. EI resultado es devuelto en una estructura de tipo rccoord.
struct rccoord
{
short row;
short cot;
} ~ar _gettextposition(void);
mimero de fila
mimero de columna
Especifica la ventana donde va a ser visualizado todo el texto. Los argu-
mentos (fl, c1) corresponden a la fila y columna de la esquina superior
izquierda de la ventana y los argumentos (f2, c2) especifican la fila y co-
lumna de la esquina inferior derecha de la ventana.
EI texto es escrito a partir de la parte superior de la ventana. Cuando
la ventana se llena se hace scroll automciticamente.
Controla si el texto cubre una nueva linea 0 se trunca cuando se alcanza
el borde de la ventana de texto definida. EI argumento opci6n puede ser
una de las constantes siguientes:
_GWRAPOFF
_GWRAPON
# include <stdio.h>
# include <graph.h >
struct videoconjig cv;
char bujjer[1255};
main( )
[
struct rccoord pos_cursor, pos_inicial,·
int color, c = 0;
/ * Se utiliza la modalidad por dejecto */
---f5etvideoconjig(&cv); / * almacenar conjiguracion */
_cka~c~en(_GCLEARSCREENt
~ettextwindow(l, 15, 14, 50); / * ventana de texto */
_wrapon(_GWRAPOFF); / * texto no continua en una nueva linea */
color = ---f5ettextcolor( ); / * guardar el color original */
~ettextcolor(color - 1);
-.5ettextposition(l, 1);
pos_cursor = ---f5ettextposition( );
pos_inicial = pos_cursor;
while (pos_cursor.row < 20)
[
/ * inicializar variable */
/ * salvar posicion inicial */
c + = sprintj(bujjer + c, "Fila = 0/02d,Col = %d  n':
pos_cursor.row+ +, pos_cursor.col);
}
~ettextposition(pos_inicial.row, pos_inicial.col);
_outtext(bujjer );
~ettextcolor(color); / * restaurar color original */
_outtext("Penultima lfnea. La siguiente linea no se trunca");
/ * Una especijicacion juera de los limites de la ventana, situa
* el cursor al principio de la ultima llnea de la misma
*/
----.Settextposition(21,51);
_wrapon(_GWRAPON); / * texto continua en una nueva llnea */
_outtext(C<  nUltima llnea. Esta Unea es demasiado larga.  n ");
J
Este programa crea una ventana para texto y escribe sobre ella. Una
vez definida la ventana, las referencias hechas a fila y columna para situar
el cursor se miden con respecto a los bordes de la ventana. Tambh~nutiliza
la funcion _wrapon( ) con el fin de ver el efecto que produce en sus dos
modalidades. Observar que no se ha definido una modalidad de graficos,
por no ser necesario cuando se trabaja solamente con texto.
Almacena en el area de memoria apuntada por imagen, la figura de la pan-
talla encerrada en un rectangulo definido por los puntos (xl, yl) y (x2, y2).
El area de memoria debe ser 10 suficientemente grande como para conte-
ner la figura. El tamafio puede ser determinado por la funcion
_imagesize( ).
void _far _getimage(short xl, short yl, short x2, short y2, char
_far *imagen);
Da como resultado el numero de bytes necesarios para almacenar la figura
definida dentro del rectangulo especificado por las coordenadas (xl, yl)
y (x2, y2). Este tamafio es determinado por la siguiente formula:
x = abs(xl - x2) + 1;
y = abs(yl - y2) + 1;
t = 4 + ((long)((x * bits~or~ixel + 7) /8) * (long)y);
EI valor de bits-por -pixel es devuelto por la funci6n
--f5etvideoconfig( ) en el campo bitsperpixel.
La fund6n _imagesize( ) devue1veel numero de bytes necesarios para
almacenar la figura.
buffer = (char *)malloc((unsigned int) _imagesize(O, 0, 80, 50));
if (buffer = = (char *)NULL) exit(-l);
Transfiere a la pantalla la figura almacenada en la zona de memoria apun-
tad a por imagen, colocando la esquina superior izquierda del rectangulo
que contiene dicha figura en el punta (x, y). EI argumento accion, es un
parametro utilizado para superponer 0 transformar imagenes, con otras
imagenes ya en pantalla.
su fund6n es opuesta a --f5etimage( ). Da lugar a una
copia exacta de la imagen almacenada.
es la misma que _GPSET, excepto que produce una
imagen negativa.
ejecuta la operad6n AND entre la imagen almacenada
y la de la pantalla. Se utiliza para transferir una ima-
gen encima de una ya existente sobre la pantalla.
ejecuta la operacion OR entre la imagen almacenada
y la de la pantalla. Se usa para superponer la imagen
sobre otra ya existente.
ejecuta la operacion XOR entre la imagen almacenada
y la de la pantalla. Es un modo especial utilizado a me-
nudo para animaci6n.
La animacion de un objeto se realiza de acuerdo con la siguiente secuencia
de pasos:
3. Borrar la imagen de la pantalla (-putimage( )) y dibujarla en la
nueva posicion.
Una imagen se borra ejecutando -putimage( ) con XOR por segunda
vez en la misma posicion. La animacion tambien puede ejecutarse utilizando
la opcion PSET, teniendo la precaucion de que una nueva imagen borre
la anterior. En este ultimo caso, el rectangulo debe ser suficiente, para que
ademas de recoger la imagen, recoja tambien el desplazamiento de la misma.
Los siguientes ejemplos, muestran con claridad 10 anteriormente ex-
puesto.
El siguiente ejemplo visualiza el resultado que se obtiene al desplazar
un bola a 10 ancho de la pantalla, utilizando los cinco modos de accion
(PSET, PRESET, XOR, OR y AND). Observar que la animaci6n real se
produce cuando se ejecuta la sentencia:
buffer almacena la matriz de pixels correspondientes a la imagen y al
desplazamiento de la misma.
/ * Funciones para animaci6n de figuras:
* _imagesize --Itetimage -putimage
*/
# include <conio.h >
# include <stddefh>
# include <stdlib.h>
# include <malloc.h >
# include <graph.h>
short accion[5J =
{ _GPSET, _GPRESET, _GXOR, _GOR, _GAND };
char ~escrip[5J =
{ "PSET': "PRESET: "XOR ': "OR ': "AND " };
main( )
(
char far *buffer;
size_t t_imagen;
short i, x, y = 0;
/ * Seleccionar la modalidad de video */
-----setvideomode(--.MAXRESMODE);
/ * Almacenar configuraci6n */
--Itetvideoco~ig(&cv);
/ * Animaci6n de figuras */
-----setcolor(3);
for (i = 0; i < 5; i+ +)
{
x = 50; y + = 35;
----settextposition(1, 1);
_outtext( descrip[i]);
/ * Dibujar y desplazar una elipse */
_ellipse(_GFILLINTERIOR, x-15, y-15, x+15, y+15);
t_imagen = (size_tJ_imagesize(x-16, y-16, x+ 16, y+ 16);
buffer = (char far *)-fmalloc(t_imagen);
if (buffer = = (char far *)NULL)
exit(!----setvideomode( ---.DEFAULTMODE));
/ * Almacenar la elipse en el buffer */
----f5etimage(x-16.y-16, x+16, y+16, buffer);
/ * Mover la elipse con una determinada acci6n */
while (x < cv.numxpixels-60)
[
x += 1;
-putimage(x-16, y-16, buffer, accion[i]);
}
-ffree(buffer );
getch( );
}
exit(!----setvideomode( ---.DEFAULTMODE));
}
/ * Liberar memoria */
/ * pulsar una tecla para continuar */
El siguiente ejemplo simula una pelota rodando. En este caso, se utiliza
la funci6n -putimage( ) con la opci6n XOR. Observar que el rectangulo
para leer la figura es ahora mas pequefio, esto es, los lados son tangentes
al circulo que forma la pelota.
3. Borrar la imagen: -putimage() con la opci6n _GXOR, en la mis-
ma 10calizaci6n del punta 1.
4. Volver al punto 1, para dibujar la imagen en la nueva localizaci6n
ca1culada.
# include <stdio.h>
# include <conio.h >
# include <graph.h >
# include <malloc.h >
struct videoconfig cv;
char *buffer; / * utilizado con ~etimage y con -putimage */
main( )
(
size_t t_imagen;
int x=O, i=O;
/ * Seleccionar la modalidad de video */
--.Setvideomode( --.MAXRESMODE);
~etvideoconfig(&cv); /* almacenar configuraci6n ;,/
_ellipse(_GFILLINTERIOR, 0, 96, 8, 104); / * dibujar pelota */
t_imagen = (size_t}_imagesize(O, 96, 8, 104);
buffer = (char *)malloc(t_imagen);
if (buffer = = (char *)NULL)
exit(!--.Setvideomode( ----.DEFAULTMODE));
1* Almacenar la imagen en el buffer */
~etimage(O, 96, 8, 104, buffer);
/ * Desplazar la pelota a 10 ancho de la pantalla */
do
(
-putimage(x, 96, buffer, _GXOR);
-putimage(x+ =2, 96, buffer, _GXOR);
for (i = 1; i < 3000; i+ +);
}
while (x < cv.numxpixels-10);
/ * borrar pelota */
/ * dibujar pelota */
/ * retardo */
getch( );
free(buffer );
/ * Pulsar una tecla para continuar */
/ * Liberar memoria */
---.Setvideomode(-,,-DEFAULTMODE);
}
EI siguiente ejemplo presenta una pe10ta que rebota al chocar contra
una barrera. Observar la utilizaci6n de la funci6n, ---!Jetpixel( ).
# include <stdio.h >
# include <conio.h >
# include <graph.h>
# include <malloc.k>
struct videoconfig cv;
char *buffer; / * utilizado con ---!Jetimage y con -putimage */
main( )
[
size_t t_imagen; / * tamano de la imagen */
iot x, i, posicion, altura, direcci6n;
iot x1=1, y1=96, x2=9, y2=104, d=x2-x1;
/ * Seleccionar la modalidad de video */
---.Setvideomode(-MAXRESMODE);
---!Jetvideoconfig(&cv); / * almacenar configuraci6n */
/ *Posici6n y altura de la barrera */
printf(HPosici6n de la barrera de 10 a %d= > ':cv.numxpixels-20);
scanj(H%d': &posici6n);
printf(HAltura de la barrera de 1a %d = > ':cv.numypixels-1);
scanj(H%d': &altura);
_clearscreen( _GCLEARSCREEN);
---.Setcolor(2);
---fectangle( _GFILLINTERIOR, posicion, 0, posicion +20, altura);
-----setcolor(l);
_ellipse(_GFILLINTERIOR, xl, y1, x2, y2); / * dibujar pelota */
t_imagen = (size_t)_imagesize(x1-1, y1-1, x2 +1, y2 +1);
buffer = (char *)malloc( t_imagen );
if ( buffer = = (char *)NULL)
exit( !-----setvideomode(---.DEFAULTMODE ) );
/ * A lmacenar la imagen en el buffer */
---f5etimage(x1-1,y1-1, x2+ 1, y2+ 1, buffer);
/ * Mover la pelota por la pantalla. Si choca contra la
* barrera rebota
*/
direcci6n = 1;
x = d;
do
{
/ * Si no hay contacto con la barrera, getpixel devuelve
* el color de fondo. En caso contrario devuelve el color
* de la barrera.
*/
if (---f5etpixel(x+3, y1-1) != 0) direcci6n = -1;
x + = direcci6n;
-putimage( x-d, y1-1, buffer, _GPSET);
for (i = 1; i < 2000; i+ +);
J
while (x < cv.numxpixels-d && (x > d II direcci6n = = 1));
/ * 1 = derecha, -1 = izquierda */
/ * coordenada x de la pelota */
/* pelota */
/ * velocidad */
getch( );
freer buffer );
/ * Pulsar una tecla para continuar */
/ * Liberar memoria */
-----setvideomode(---.DEFAULTMODE);
J
Realizar un programa que simule los movimientos de una bola rodan-
do sobre una mesa de billar.
Amilisis:
dibujar la bola
utilizar ---$etimage( ) para almacenar la bola
hacer PosicionActual = PosicionAnterior = PuntoDeComienzo
DO
Borrar (-putimage( ) con XOR) la figura de la posicion anterior
PosicionActual = PosicionActual + Incremento
Visualizar (-putimage( ) la figura en la posicion actual
Esperar un tiempo pequeno
hacer PosicionAnterior = PosicionActual
WHILE no se pulse una tecla
fin del programa
# include <stdio.h >
# include <conio.h >
# include <graph.h >
# include <ma/loc.h >
# include <stdlib.h >
struct videoconjig cv;
char ~ar *bola;
main( )
[
size_t t_imagen; / * tamafio de la imagen */
short retar, Max----.X, Max_Y, Min----.X, Min_Y,·
short RadioEola, Inicio----.X, Inicio_Y,·
short PosicionActual----.X, PosicionActual_Y,·
short PosicionAnterior ----.X,PosicionAnterior -Y,.
short Incremento----.X, Incremento_Y, Direccion----.X,Direccion_Y,·
/ * Seleccionar la modalidad de video */
---.Setvideomode(--.MAXRESMODE);
---$etvideoconjig(&cv); / * almacenar conjiguracion */
---.Setbkcolor(_GREEN);
/ * Valor minimo y maximo de las coordenadas de panta/la */
Max----.X = cv.numxpixels-l; Min----.X = 0;
Max_Y = cv.numypixels-l; Min_Y = 0;
/ * Dibujar l[mites de la pantalla */
---fectangle(_GBORDER, Min---.X, Min_Y, Max---.X, Max_Y);
/ * Fijar el radio de la bola */
RadioBola = 12;
/ * Fijar la posicion inicial de la bola */
Inicio----.X = RadioBola + 1;
Inicio_Y = RadioBola + 1;
/ * Dibujar la bola sobre la pantalla */
_ellipse( _GFILLINTERIOR,
Inicio----.X - RadioBola, Inicio_Y - RadioBola,
Inicio----.X + RadioBola, Inicio_Y + RadioBola);
/ * Almacenar la figura en el array bola */
t_imagen = (size_tJ_imagesize(
Inicio----.X - RadioBola, Inicio_Y - RadioBola,
Inicio----.X + RadioBola, Inicio_Y + RadioBola);
bola = (char *)malloc(t_imagen);
if (bola = = (char *)NULL)
exit(L..setvideomode( --.DEFAULTMODE));
/ * A lmacenar la imagen en bola */
~etimage(Inicio----.X - RadioBola, Inicio_Y - RadioBola,
Inicio----.X + RadioBola, Inicio_Y + RadioBola, bola);
/ * Inicializacion */
PosicionActual----.X = Inicio---.X,·
PosicionActual_Y = Inicio_Y;
PosicionAnterior ----.X = Inicio----.X- RadioBola;
PosicionAnterior_Y = Inicio_Y - RadioBola;
Direccion----.X = 1;
Direccion_Y = 1;
/ * 1 = derecha, -1 = izquierda */
/ * 1 = hacia abajo, -1 = hacia arriba */
do
[
/ * Borrar la bola anterior */
-putimage(PosicionAnterior ---.X, PosicionAnterior_ Y,bola,_GXOR);
/ * Calcular la nueva posicion de X
* si borde derecho poner direccion hacia borde izquierdo
.* si borde izquierdo poner direccion hacia borde derecho
* si la bola choca con un borde, realizar un pitido
*/
Incremento----..X = rand( ) % RadioBola;
if (PosicionActual----..X+ Incremento----..X+ 2 * RadioBola > Max----..X)
(
Direccion----..X= -1; putchart  x07');
J
if (PosicionActual----..X- Incremento----..X < Min_YJ
(
Direccion----..X= 1; putchart  x07');
J
PosicionActual~ =PosicionActual~ + (Incremento~ *Direccion~);
/ * Calcular la nueva posicion de Y
* si borde inferior poner direccion hacia borde superior
* si borde superior poner direccion hacia borde inferior
* si la bola choca con un borde, realizar un pitido
*/
Incremento_Y = rand( ) % RadioBola;
if (PosicionActual_Y + Incremento_Y + 2 * RadioBola > Max_Y)
(
Direccion_Y = -1; putchart  x07');
J
if (PosicionActual_Y - Incremento_Y < Min_Y)
(
Direccion_Y = 1; putchart  x07');
J
PosicionActual_ Y=PosicionActual_ Y+(Incremento_ Y*Direccion_ Y);
* Visualizar la bola en la nueva posicion */
-putimage(PosicionActual-.-X, PosicionActual_Y, bola, _GXOR);
for (retar = 1; retar < 4000; retar+ +); / * retardo */
/ * La posicion actual pasa a ser posicion anterior */
PosicionAnterior ----..X= PosicionActual----..X;
PosicionAnterior _Y = PosicionActual_Y;
J
while (!kbhit( )); / * repetir hasta pulsar una tecla */
---setvideomode( ----DEFAULTMODE);
}
Las funciones que refieren sus coordenadas a un sistema de coordenadas
fisico 0 16gico, requieren valores enteros. En ocasiones necesitaremos re-
presentar valores reales y dentro de unos limites. Estos valores, al represen-
tarlos utilizando toda la pantalla 0 una ventana, necesitanin en la mayoria
de los casos de la aplicaci6n de un factor de escala. La funci6n
---setwindow( ) permite de una forma sencilla realizar estas operaciones.
Define un sistema de coordenadas gnificas reales (coordenadas cartesia-
nas) sobre una ventana 0 en su defecto sobre toda la pantalla. Los argu-
mentos (wxl, wyl), especifican la esquina superior izquierda de la ventana
sobre la que se encuadra el sistema de coordenadas y los argumentos (wx2,
wy2), especifican la esquina inferior derecha de esta ventana. El origen de
coordenadas es el (0, 0). El argumento inver puede tomar como valores:
establece el sistema de coordenadas cartesianas haciendo que
"y" aumente de abajo a arriba de la pantalla.
establece el sistema de coordenadas cartesianas haciendo que
"y" aumente de arriba a abajo de la pantalla.
short ~ar ----setwindow(shortinver, double wxl, double wyl, double
wx2, double wy2);
Esta funci6n devuelve un valor distinto de 0 si se ejecuta satisfactoria-
mente 0 un 0 en caso contrario.
# include <stdio.h >
# include <conio.h >
# include <graph.h >
# include <math.h >
# define TR UE 1
main( )
(
double pi, alja, valor1, valor2, paso, radio, X Y,.
/ * Seleccionar la modalidad de video */
---.Setvideomode(--.MAXRESMODE);
pi = atan(J.O) * 4;
valor1 = 5.0; valor2
/ * definir el valor del mimero pi */
6.0; paso = 1000.0;
/ * Sistema de coordenadas reales */
---.Setwindow(TRUE, -1, -1, 1, 1);
/ * Representaci6n grdfica */
for (alja = 0; alja < = 2 * pi; alja + = 2 * pi / paso)
(
radio = cos(2 * alja);
x = radio * cos(valor1 * alja);
Y = radio * sin(valor2 * alja);
---.Setpixel_w(X Y);
}
getch( ); / * Pulsar una tecla para continuar */
---.Setvideomode(---.DEFAULTMODE); /* configuraci6n inicial */
}
# include <stdio.h>
# include <conio.h >
# include <graph.h >
# include <math.h >
# define TR UE 1
main( )
{
double X-ftlin, X-ftlax, Y-ftlin, Y-ftlax, x: Y, incremento;
printjttEntre que valores de X esta comprendida la funci6n  n");
printj("X mfnima X maxima: ");
scanf("%lj %lj': &X-ftlin, &X-ftlax);
printj("Entre que valores de Y esta comprendida la funci6n  n ");
printj("Y mfnima Y maxima : ");
scanf("%lj %lj': &Y-ftlin, &Y-ftlax);
/ * Seleccionar la modalidad de vfdeo */
---setvideomode( --.MAXRESMODE);
~etvideoconfig(&cv); / * almacenar configuraci6n */
/ * Establecer el sistema de coordenadas cartesianas */
---setwindow( TRUE, X-ftlin, Y-ftlin, X-ftlax, Y_max );
/ * Dibujar ejes */
--"loveto_w(X--"lin, 0);~ineto_w(X--"lax, 0);
--"loveto_w(O, Y--"lin); ~ineto_w(O, Y--"lax);
/ *Dibujar eje X */
/ * Dibujar eje Y */
/ * Representaci6n grafica */
incremento = (X_max - X-ftlin) / cv.numxpixels;
for ( X = X_min; X < = X-ftlax; X + = incremento)
(
/ * Funci6n a representar */
Y = 2 * pow(cos(X), 2) - sin(5 + X);
---setpixel_w(X, Y); /* dibujar el punto (x, y) */
}
getch( ); / * Pulsar una tecla para continuar */
---setvideomode(-DEFAULTMODE); /* conjiguraci6n inicial */
}
/ * La funci6n matherr es automaticamente llamada si ocurre
* un error en una funci6n matematica.
*/
int matherr(struct exception ~rr)
(
printf(HError en funci6n: %s(%g)  n': err->name, err->argJ);
printf(HPulse una tecla para continuar"); getch( );
exit( ---setvideomode(-DEFAULTMODE) );
}
En estos ejemplos, los valores dentro de los limites establecidos, se re-
presentan a escala sobre toda la pantalla. Si quisieramos que la representa-
cion ocurriera sobre una ventana determinada, habria que definir previa-
mente esta por medio de la funcion ---setviewport( ).
struct _wxycoord
{
double wx;
double wy;
} _far _getwindowcoord(short x, short y);
coordenada cartesiana x
coordenada cartesiana y
coordenadas j{sicas
FUNCIONES PARA UN SISTEMA DE COORDENADAS
CARTESIANAS (WINDOW)
Cuando se define un sistema de coordenadas cartesianas para trabajar con
valores reales, tenemos que utilizar funciones cuyos panimetros sean rea-
les. Todas estas funciones finalizan con _w 0 con _wxy.
Estas funciones ya han sido comentadas anteriormente, pero pensan-
do en un sistema de coordenadas ffsico (coordenadas de pantalla) 0 16gico
(el origen 10situamos sobre un punta cualquiera de la pantalla). A conti-
nuaci6n las exponemos, pensando ahara en un sistema de coardenadas car-
tesiano (---setwindow( ). Las declaraciones para todas estas funciones es-
tan en el fiehero graph.h.
Muchas de estas funciones utilizan 0 devuelven valores definidos en
una estructura de tipo:
struct _wxycoord
[
double wx;
double wy;
};
coordenada cartesiana x
coordenada cartesiana y
struct xycoord ~ar _getviewcoord_wxy(struct _wxycoord ~ar
pwxy);
short _far -fectangle_w(short c, double wxl, double wyl, double
wx2, double wy2);
short _far -fectangle_wxy(short c, struct _wxycoord pwxyl, struct
_wxycoord pwxy2);
short ~ar _ellipse_w(short c, double wxl, double wyl, double wx2,
double wy2);
short _far _ellipse_wxy(short c, struct _wxycoord pwxyl, struct
_wxycoord pwxy2);
short _far _arc_wxy(struct _wxycoord pwxyl, struct _wxycoord
pwxy2, struct _wxycoord pwxy3, struct _wxycoord pwxy4);
short _far _pie_wxy(short c, struct _wxycoord pwxyl, struct
_wxycoord pwxy2, struct _wxycoord pwxy3, struct _wxycoord
pwxy4);
void _far _getimage_w(double wxl, double wyl, double wx2, double
wy2, char _far *imagen);
void _far _getimage_wxy(struct _wxycoord pwxyl, struct
_wxycoord pwxy2, char _far *imagen);
long _far ~magesize_w(double wxl, double wyl, double wx2,
double wy2);
long _far ~magesize_wxy(struct _wxycoord pwxyl, struct
_wxycoord pwxy2);
void ~far _putimage_w(double wxl, double wyl, double wx2,
double wy2, char _far *imagen, short accion);
El siguiente ejemplo representa un conjunto de valores sobre un siste-
ma de coordenadas cartesianas. La representaci6n se hace sobre tres venta-
nas cuadriculadas de diferentes tamafios (efecto ampliar/reducir), incluyendo
texto.
# include <stdio.h >
# include <conio.h >
# define TR VE 1
#define FALSE 0
struct videoconfig cv;
void cuadricular_dibujar(void);
double val! J = ( -0.3, -0.2, -0.224, -0.1, -0.5, 0.21, 2.9,
0.3, 0.2, 0.0, -0.885, -J.1,-0.3, -0.2,
0.001, 0.005, 0.14, 0.0, -0.9, -0.13, 0.3
};
main( )
(
void cuadricular_dibujar(void);
int xmedia, ymedia;
int pejex, pejey, cols, fi/as;
struct _wxycoord esizda, eidcha;
---setvideomode( --.MAXRESMODE);
---1Jetvideoconfig(&cv); / * almacenar configuraci6n */
_clearscreen( _GCLEARSCREEN);
pejex = cv.numxpixels;
pejey = cv.numypixels;
xmedia = pejex / 2;
ymedia = pejey / 2;
cols = cv.numtextcols;
fi/as = cv.numtextrows;
---setviewport(O, 0, xmedia-l, ymedia-1);
---settextwindow(l, 1, filas/2, cols/2);
---setwindow(FALSE, -2.0, -2.0, 2.0, 2.0);
cuadricular _dibujar( );
~etviewport(xmedia, 0, pejex-I, ymedia-I);
~ettextwindow(I, eols/2 +1, ji/as/2, cols);
~etwindow(FALSE, -3.0, -3.0, 3.0, 3.0);
euadrieular_dibujar( );
-Teetangle_w(_GBORDER, -3.0, -3.0, 3.0, 3.0);
~etviewport(O, ymedia, pejex-I, pejey-I);
~ettextwindow(fi/as/2 +2, 1, ji/as, cols);
~etwindow(TRUE, -3.0, -1.5, 1.5, 1.5);
euadrieular_dibujar( );
esizda.wx = -3.0; esizda.wy = -1.5;
eideha.wx = 1.5; eideha.wy = 1.5;
_reetangle_wxy(_GBORDER, &esizda, &eideha);
geteh( ); / * Pulsar una tecla para eontinuar */
~etvideomode(----.DEFAULTMODE); / * eonjiguraci6n inicial */
J
void euadrieular_dibujar(void)
{
int i, neolores, xl, yI, x2, y2;
double x, y;
char texto[80};
for (i = 1; i < neolores; i+ +)
{
~ettextposition(i, 2);
~ettexteolor(i);
sprintj(texto, HColor O/Od':i);
_outtext(texto );
J
---setcolor(l);
-.Jectangle_w(_GBORDER, -1.0, -1.0, 1.0, 1.0);
-.Jectangle_w(_GBORDER, -1.02,-1.02,1.02, 1.02);
for (x = -0.9, i = 0; x < 0.9; x + = 0.1)
(
---setcolor(2);
---fl1.oveto_w(x, -1.0);
_lineto_w(x, 1.0);
---fl1.oveto_w(-1.0, x);
_lineto_w( 1.0,x);
---setcolor(3);
---fl1.oveto_w(x - 0.1, valli+ +J);
_lineto_w(x, valli]);
}
---fl1.oveto_w(0.9, valli + +J);
_lineto_w(1.0, valli]);
}
El resultado que se obtiene al ejecutar este programa se muestra en
la figura siguiente:
I .,~.~n
1~l)lor ;:
Color J
Color" :
Coler 5 I'
Coler G !L
r.nl.l' 7 i,-
CIJlul' 8
l~ill.Rr ~
Color 10 i
Coler 11
Coler lZ 'L. -
Coler 13
r.nl.l' 14
Cul.r 15
I~olo! ;:
Color J
Color"
r.nloT·!i
Cul.r ,
r.nl.r 7
CIJlul' 8
Color !I
Color 10
Color 11
r.nl.r 12
Cul.r 13
r.nl.r 15
J .•• ll ,ur
L:Dlor ~
Color 3
[;0111I' !I
Go I.. 1i
Col.' Ii
r.nl. i
Culur B
r.nlnr ~
Color 10
[;01.' 11
Gol.. lZ
CDI•• 13
r.nl.14
Cui •• I!I
.I
-=
__ 1.... ,"","'.'
~ __ '~,L
.. . IT""
~-···r··--I
I
( I
I
l 
II
,I
, II
Microsoft C dispone de unas pocas funciones que permiten presentar gni-
ficamente un conjunto de datos. Una presentaci6n gnifica se puede hacer
utilizando 10ssiguientes tipos de diagramas: diagrama de barras horizon-
tales, diagrama de barras verticales, diagrama de sectores, diagrama de li-
neas y diagram a de puntos.
ESTRUCTURA DE UN PROGRAMA PARA PRESENTACIONES
GRAFICAS
Para escribir un programa C que utilice funciones para presentaciones gra-
ficas, seguir 10s siguientes pasos:
1. Incluir 10s ficheros GRAPH.H y PGCHART.H, asi como cual-
quier otro fichero .h que necesite el programa.
2. Activar la modalidad de video para grcificos (----.Setvideomode())
e inicializar el sistema de presentaciones grcificas,
-pg_initchart( ).
3. Almacenar en una estructura de tipo chartenv,los parametros que
definen la presentacion grafica sobre la pantalla. La funcion
-pg_dejaultchart( ) los asigna por defecto.
La definicion de esta estructura, as! como de las estructuras
que la componen, se encuentran declaradas en el fichero pgchart.h.
Todas estas estructuras pueden visualizarse facilmente a traves del
menu de ayuda del PWB de Microsoft C; sera necesario acceder
a sus miembros cuando deseemos modificar los valores asignados
por defecto.
typedef struct
[
short charttype; / * JGJAR, JG_COLUMN, JGJINE,
JG-.SCATTER, JGJIE "*/
short chartstyle; / * Estilo para el tipo de grdfico
seleccionado */
windowtype chartwindow; / * Definicion de la ventana para
el grdfico completo */
windowtype datawindow; / * Definicion de la ventana para
la parte de datos del grdfico */
titletype main title; / * TItulo principal del grdfico */
titletype subtitle; / * Subtftulo del grdfico */
axistype xaxis; / * Definicion para el eje X */
axistype yaxis; / * Definicion para el eje Y */
legendtype legend; / * Definicion para la leyenda */
J chartenv;
4. Almacenar los datos a representar en arrays, ya que las funciones
para presentaciones graficas los referencian mediante punteros. Los
datos pueden provenir de diferentes medios: de ficheros, de cal-
culos 0 directamente del teclado.
Estas funciones devuelven un 0 si se ejecutan satisfactoriamente y un
valor distinto de 0, en caso contrario.
Inicializa el color y estilos de linea, paletas, modos de pantalla y tipos de
caracteres. Esta funcion debe ser la primera en llamarse.
Inicializa por defecto todas las variables contenidas en una estructura de
tipo chartenv, necesarias para el gnifico a realizar.
Constante
predefinida
_PG_BARCHART 1
_PG_COLUMNCHART 2
_PG_LINECHART 3
_PG_SCATTERCHART 4
.-PG_PIECHART 5
Tipo barras horizontales
Tipo barras verticales
Tipo lineas
Tipo puntos
Tipo sectores
estilo es un valor 1 0 2. Cada uno de los cinco tipos de gnificos, puede
aparecer en dos estilos diferentes:
Barras H.
Barras V.
Lineas
Puntos
Sectores
Lado a lado
Lado a lado
Puntos con lineas
Puntos con lineas
Con porcentajes
Apiladas
Apiladas
Puntos solamente
Puntos solamente
Sin porcentajes
Las constantes asociadas a los valores 1 y 2 para cada esti-
10, son las siguientes:
Constante
predefinida Valor Significado
PG_PLAINBARS 1 Estilo Barras lado a lado
_PG_STACKEDBARS 2 Estilo Barras apiladas
_PG_POINTANDLINE 1 Estilo Puntos y Lineas
_PG_POINTONLY 2 Estilo Puntos solamente
_PG_PERCENT 1 Estilo Sectores con 070
_PG_NOPERCENT 2 Estilo Sectores sin %
Presenta un diagrama para una unica serie de datos. El diagrama puede
ser de barras 0 de lineas, dependiendo esto del tipo especificado en la es-
tructura ent.
short _far _pg_chart(chartenv _far * ent, char * _far * elementos,
float _far * valores, short n);
elementos array que contiene los elementos para los cuales se quieren re-
presentar los valores. Por ejemplo, paises, empresas, meses.
array que contiene los datos que queremos representar gnifi-
camente y que se corresponden con los elementos anteriores.
short _far _p~chartpie(chartenv _far * ent, char * _far * elementos,
float _far * valores, short _far *explotar, short n);
elementos array que contiene los elementos para los cuales se represen-
tan los valores.
explotar array de n valores 0 6 1.Un 1indica separar (explotar) ese sec-
tor de los otros. Un 0 indica no separarlo.
# include <conio.h >
# include <stdlib.h >
# include < graph.h >
# include <string.h>
# include <pgchart.h>
# define PAISES 6
float ~ar valor[PAISESj = {53.1, 41.8, 19.5, 13.7, 10.8, 20.(jJ;
char ~ar *elementos[PAISESj =
{"Italia': "Espana': "Grecia': "Tunicia': "Turqu{a': "Otras"};
short ~ar explotar[PAISESj = { 0, 1, 0, 0, 0, °};
main( )
{
/ * Modo grajico de mas alta resoluci6n */
if (!---setvideomode(-MAXRESMODE))
exit( 1 ); / * Grajicos no disponibles */
-pg_dejaultchart(&ent, ~G~ARCHART, ~G~LAINBARS);
strcpy(ent.maintitle.title, "Producci6n de aceite de oliva");
-pg_chart(&ent, elementos, valor, PAISES);
getch( );
_clearscreen( _GCLEARSCREEN);
-pg_dejaultchart(&ent, ---.PG_COWMNCHART, ---.PG~LAINBARS);
strcpy(ent.maintitle.title, "Producci6n de aceite de oliva");
-pg_chart(&ent, elementos, valor, PAISES);
getch( );
_clearscreen( _GCLEARSCREEN);
-pg_dejaultchart(&ent, ~G~IECHART, ~G~ERCENT);
strcpy(ent.maintitle.title, "Producci6n de aceite de oliva");
-pg_chartpie(&ent, elementos, valor, explotar, PAISES);
getch( );
exit(!---setvideomode(---.DEFAULTMODE));
}
_P9_chartms (ent,e Iementos,va I0res, nse rieS,n ,co Iumnas,
eti_series)
Genera un gnifico para multiples series de datos. El diagrama puede ser
de barras, de lineas, 0 de puntos, dependiendo esto del tipo especificado
en la estructura ent.
short ~ar _p~chartms(chartenv _far * ent, char * _far * elementos,
float ~ar * valores, short nseries, short n, short columnas, char * _far
* etL...series);
elementos array que contiene los elementos para los cuales se represen-
tan los valores.
eti_series array de etiquetas correspondientes a los valores que se indi-
can en cada serie.
# include <conio.h >
# 'include <graph.h >
# include <string.h >
# include <pgchart.h>
# include <stdlib.h >
/ * Observar que los datos son declarados en un array
* multidimensional. Como las funciones para representaciones
* graficas multiples esperan arrays simples, habra que
* emplear un tipo cast en la llamada a la funcion.
*/
#define EQUIPOS 4
# define MESES 3
float _far valores[EQUIPOSj[MESESj = {[ 453, 522, 617 ],
{ 503, 440, 585 ],
{ 713, 642, 477 ],
{ 392, 464, 411 )1.
char _far *meses[MESESj = { "Mayo':"]unio':"]ulio" ];
char _far ~quipos[EQUIPOSj = {t~lfa':"Verdes':"]avis':t~tlas"];
main( )
{
chartenv ent;
/ * Modo grafico de mas alta resolucion */
if (L...setvideomode(--.MAXRESMODE))
exit(l); / * Graficos no disponibles */
-pg_defaultchart(&ent, ---.PG----.BARCHART, ---.PG---.PLAINBARS);
strcpy(ent.maintitle.title, "Registros liga de Golf");
-pg_chartms(&ent, meses, (float _far *)valores,
EQUIPOS, MESES, MESES, equipos);
getch( );
_clearscreen( _GCLEARSCREEN);
-pg_defaultchart(&ent, -PG_COWMNCHAKI; -PG---.PLAINBARS);
strcpy(en t.main title.title, "Registros liga de Golf");
-pg_chartms(&ent, meses, (float _far *)valores,
EQUIPOS, MESES, MESES, equipos);
getch( );
_clearscreen( _GCLEARSCREEN);
-pg_dejaultchart(&ent, ~G---LINECHAKF, ~G~OINTANDLlNE);
strcpy(en t.main title.title, HRegistros liga de Golf");
-pg_chartms(&ent, meses, (float _far *)valores,
EQUIPOS, MESES, MESES, equipos);
getch( );
_clearscreen( _GCLEARSCREEN);
/ * Diagrama de lfneas multiple mostrando solamente dos
* columnas de las tres y tres series de las cuatro
*/
-pg_dejaultchart(&ent, ~G---LINECHAKF, ~~OINTANDLlNE);
strcpy(en t.main title.title,HRegistros parciales liga de Golf");
-pg_chartms(&ent, &meses[l], &valores[l][l],
EQUIPOS- 1, MESES - 1, MESES, &equipos[lJ);
getch( );
exit(L---setvideomode( --.DEFAULTMODE));
}
short ~ar _p~chartscatter(chartenv _far * ent, float _far * xvalores,
float ~ar * yvalores, short n);
_P9_chartscatte rms (e nt,xva I,yva I,n se rieS,n ,CO Ium nas,
eti_series)
short _far _p~chartscatterms(chartenv ~ar * ent, float _far * xva-
lores, float _far * yvalores, short nseries, short n, short columnas, char
* _far * eti~eries);
eti_series array de etiquetas correspondientes a cada una de las series re-
presentadas.
# include <conio.h >
# include <graph.h >
# include <string.h >
# include <std/ib.h>
# include <pgchart.h>
# define VALORES 5
# define SERIES 2
float _far empleados[SERIES][VALORES]
{ {235, 423, 596, 729, 963 },
{ 285, 392, 634, 801, 895 } };
float _far beneficios[SERIES][VALORES] =
{ {0.9, 2.3, 5.4, 8.0, 9.3 },
{ 4.2, 3.4, 3.6, 2.9, 2.7 } };
char ~ar ~mpresas[SERIES] = { "Industrias FJC':
"Construcciones C"};
main( )
[
chartenv ent;
/ * Modo grdfico de mds alta resolucion */
if (L...setvideomode( --.MAXRESMODE))
exit(l); / * Grdficos no disponibles */
-pg_dejaultchart (&ent,---.PG~CATTERCHAKI; ---.PG-YOINTONLY},o
strcpy(en t.main title.title, "Industrias FIC");
strcpy(ent.xaxis.axistitle. title, "Empleados' ');
strcpy(ent.yaxis.axistitle.title, "Beneficios' ');
/ * La siguiente, son datos para formar la escala del eje x */
ent.xaxis.scalefactor=1000; / *factor de escala:x10, ... */
strcpy(en t.xaxis.scaletitle. title,"x 1000");
ent.xaxis.autoscale=O; /* l=escala automdtica, O=usuario */
ent.xaxis.scalemin = 0.0; / * lImite inferior */
ent.xaxis.scalemax = J.O; / * lImite superior */
ent.xaxis.ticinterval = 0.1; / * intervalo */
ent.xaxis.ticfClrmat=l; / *formato (0 0 1) */
ent.xaxis.ticdecimals=l; / * mimero de decimales */
-pg_chartscatter(&ent, empleados[O], beneficios[O], VALORES);
getch( );
_clearscreen( _GCLEARSCREEN);
-p~dejaultchart (&ent,---.PG~CATTERCHAKF, ---.PG-YOINTONLY},o
strcpy(ent.xaxis.axistitle. title, "Empleados' ');
strcpy(ent.yaxis.axistitle.title, "Beneficios' ');
/ * Lo siguiente, son datos para formar la escala del eje x */
ent.xaxis.scalefactor=1000; / *factor de escala:x10, ... */
strcpy(en t.xaxis.scaletitle. title,"x 1000");
ent.xaxis.autoscale = 0;
ent.xaxis.scalemin = 0.0;
ent.xaxis.scalemax = 1.0;
ent.xaxis.ticinterval = 0.1;
ent.xaxis.ticformat = 1;
ent.xaxis.ticdecimals = 1;
/ * 1= escala automdtica, 0= usuario */
/ * limite inferior */
/ * limite superior */
/ * intervalo */
/ *formato (0 0 1) */
/ * mimero de decimales */
-pg_~hartscatterms(&ent, (float _far *)empleados,
(float _far *)beneficios,
SERIES, VALORES, VALORES, empresas);
exit(L ...setvideomode( ~EFA ULTMODE));
1
Un font es un tipo determinado de letra, como Courier 0 Roman, que pue-
de escribirse en varios tamafios. Por ejempl0, "Courier 15 x 12" indica
texto donde cada canicter es de tipo Courier y ocupa un area de pantalla
igual a 15 puntos verticales por 12 puntos horizontales.
Los datos para poder configurar 10s distintos tipos de letras se encuen-
tran en 10s ficheros que tienen extensi6n .FON. El nombre del fichero indi-
ca el tipo de letra.
Para visualizar un texto con un determinado tipo de letra, realizar 10s
siguientes pasos:
1. Registrar 10s fonts disponibles (* .FON), en una lista en memoria,
mediante la funci6n -,"egisterfonts( ).
2. Llamar a la funci6n -..Setfont( ) para seleccionar un determinado
tipo de letra.
3. Situarse en la posici6n deseada de la pantalla con la funci6n
_moveto( ) y visualizar el texto utilizando la funci6n _outgtext( ).
Lee la informaci6n de cabecera de los ficheros .FON especificados y cons-
truye una lista cuya finalidad es dar informaci6n de los ficheros .FON dis-
ponibles.
Esta funci6n devuelve como resultado el numero de fonts registrados
en la lista en memoria 0 un valor negativo si ocurre un error.
Busca un tipo de letra (font) que coincida con el conjunto de caracteristi-
cas especificado y hace que este sea e1tipo de letra actual.
indica el nombre del font elegido, el cual puede ser: cou-
rier, helv, tms rmn, modern, script 0 roman.
si un font del tamafi0 especificado no esta registrado, se
selecciona el font mas apropiado de los registrados. Si al
menos hay un font registrado, se utiliza. Si esta opci6n no
se especifica y el font elegido no coincide exactamente, ocu-
rre un error.
selecciona el font numero x, donde x es menor 0 igual que
el valor devuelto por la funci6n _registerfonts( ).
Esta funci6n devuelve el valor 0 si se ejecuta satisfactoriamente y -1
en caso contrario.
Microsoft C utiliza dos metodos para crear tipos de letras (bit-mapping
y vector-mapping). La primera tecnica genera los tipos Courier, Helv y Tms
Rmn a traves de map as de bits, esto es cada bit en el mapa se corresponde
con un pixel de la pantalla. La segunda tecnica genera los tipos Modern,
Script y Roman a traves de un mapa de vectores, representando cad a ca-
racter en terminos de lineas y arcos.
Tipo Mapa Tamafio en pixels Espaciado
courier bit 10 x 8, 12 x 9, 15 x 12 fijo
helv bit 10 x 5, 12 x 7, 15 x 8 proporcional
18 x 9, 22 x 12, 28 x 16
tms rmn bit 10 x 5, 12 x 6, 15 x 8 proporcional
16 x 9, 20 x 12, 26 x 16
modern vector a escala proporcional
script vector a escala proporcional
roman vector a escala proporcional
Devue1veel ancho que se requiere para escribir con la funcion _outgtext( )
el texto en e1tipo de letra actual.
Esta funcion devuelve e1ancho en pixels del texto a visualizar, 0 un
-1 si e1font no existe.
Esta funcion devuelve en una estructura de tipo -fontinjo, las carac-
teristicas del font actual.
struct ~ontinfo
{
int
int
int
int
int
char
char
};
type;
ascent;
pixwidth;
pixheight;
avgwidth;
filename[81];
facename[32];
/ * metodo (bit/vector) para crear letras */
/ *pixels desde la cima hasta la base */
/ * ancho del cardcter en pixels */
/ * alto del cardcter en pixels */
/ * anchura media de los caracteres */
/ * nombre del jichero incluyendo camino */
/ * nombre del tipo de letra (font) */
Visualiza el texto sobre la pantalla en el tipo de letra actual y en la posicion
definida por -setgtextvector( ).
( 0, 0)
( 1, 0)
( 0, 1)
(-1, 0)
( 0,-1)
no cambia.
" texto horizontal (por defecto).
rota 90 grados en sentido contrario alas agujas del reloj.
rota 180grados.
rota 270 grados en sentido contrario alas agujas del reloj.
# include <conio.h >
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <graph.h >
unsigned char *textos[NFUENTESj
{
«Courier': «Helvetica': «Times Roman': «modern': «Script': «Roman"
};
unsigned char *tipos[NFUENTESj =
{
int main(void)
{
unsigned char lista[20j;
char dir-font[~AX----.PATHj; / *directorio donde estan los FONTS */
struct videoconfig cv; / * configuracion de v{deo */
struct -fontinfo info-font; / * informacion sobre los FONTS */
short nfont, x, y;
,
/ * Inicializar el sistema grafico para tipos de letras.
* Leer la informacion de cabecera de todos los .FON
*/
if (_registerfonts( fC*.FON" ) < = 0)
[
puts(fCEscribir el camino completo para los ficheros *.FON·");
gets(dir-font); / *p. e.: C:  C600  SOURCE  SAMPLES */
strcat(dir -font, fC   *.FON");
if (_registerfonts(dir -font) < = 0)
[
putst'Error: *.FON no pueden ser cargados");
"exit(l);
l
l
/ * Modo grafico de mas alta resolucion */
if (!----.Setvideomode(~AXRESMODE))
exit(l); / * Graficos no disponibles */
/ * Poner la configuracion en cv */
---$etvideoconfig( &cv);
/ * Visualizar cada tipo de letra centrado en la pantalla */
for (nfont = 0; nfont < NFUENTES; nfont+ +)
{
/ * Construir cadena de tipos */
strcat(strcat(strcpy(lista, fC t' "), tipos[nfontJ), fC , ");
strcat(lista, fCh30w24b");
_clearscreen( _GCLEARSCREEN);
if (~etfont(lista) > = 0)
{
if (---$etfontinfo(&info-font))
{
_outtext(HError: No se puede cargar informacion ");
break;
}
1* Centrar el texto en funcion de su longitud *1
x = (cv.numxpixelsI2) - (---$etgtextextent(textosfnfontJ)12);
y = (cv.numypixelsI2) + (---$etgtextextent(textosfnfontJ)12);
~oveto(x, y);
if (cv.numcolors > 2)
~etcolor(nfont + 1);
1* Rotar y visualizar el texto *1
--setgtextvector(L ~;
_outgtext(textosfnfont J);
~etgtextvector( 0, 1);
_outgtext(textosfnfont J);
--setgtextvector( -1, 0);
_outgtext(textosfnfont J);
~etgtextvector( 0, -1 );
_outgtext(textosfnfontJ);
getch( );
}
else
_outtext(HError: font no encontrado");
}
_unregisterfonts( );
exit(!~etvideomode( --.DEFAULTMODE));
}
PARTE
7
Entorno Integrado de Desarrollo
• Utilizaci6n del PWB
• Instalaci6n de Microsoft C
PWB (Programmer's WorkBench) es un entorno de programaci6n basado
en ventanas, que incorpora un editor de textos, un compilador, un enlaza-
dor, un depurador, la utilidad Make, un analizador de c6digo fuente y un
sistema de ayuda en linea.
sfr representa una serie de 6rdenes que seran ejecutadas en
el momenta de arrancar PWB.
Impide tanto las inicializaciones como la lista de los ficheros
ultimamente accedidos.
Cuando se entra en el entorno de programaci6n, mediante la orden PWB,
10 primero que aparece es el menu principal y la ventana de edici6n. Las
partes que componen la pantalla del PWB se detallan a continuaci6n.
I. : •
Help: fseek
"Description. "ExaMpl~ -4lJ~<ontents. "Inde~ "Back.
Syntax: int fseek( FILE wstreaM, long offset, int origin ).
origin: SEEK_CUR. SEEK_END. SEEK_SET
II ••• ·S~I~'S"'II;J·qa;HI§~'••
fseek(pf, desp, SEEK_SET);
fread(areg, bytesreg, 1. pf);
printf("NoMbre: %s'n", reg.noMbre);
printf("Nota: %d'n'n", reg.nota);
}
/w Si se ha pulsado una tecla no valida w/
if (!c) fflush(stdin);
Es la primera linea; en ella se visualizan los nombres de los menus dispo-
nibles.
Aparece en la esquina superior izquierda de la ventana, cuando hay mas
de una ventana abierta. Sirve para cerrar la ventana utilizando el raton.
Aparece en la esquina superior derecha de la ventana, cuando hay mas de
una ventana abierta. Sirve para ampliar al maximo la ventana por media
del raton.
Cada ventana activa visualiza dos barras de scroll para utilizar con el ra-
ton; la barra de scroll vertical esta situada a la derecha de la ventana y la
barra de scroll horizontal esta situada en el fondo de la ventana.
En la parte superior de cad a ventana aparece el nombre del fichero con
el que estamos trabajando.
Es la ultima linea de la pantalla de PWB. Visualiza diversos tipos de infor-
macion:
Teclas titiles. FI = Ayuda, Alt = Menu, F6 = Cambiar de ventana.
Indicador de tipo de fichero.
C: fichero fuente C
text: cualquier otro fichero creado por el usuario
pseudo: pseudofichero (area de memoria que nunca es escrita so-
bre el disco)
Indicadores de estado.
C: mayusculas activadas
L: no se utiliza el CR para finalizar una linea
M: el fichero ha sido modificado
N: teclado numerico activado
0: modo sobreescritura
R: fichero para solo leer
T: fichero temporal
X: se esta registrando una macro
El menu principal consta de nueve opciones: File, Edit, View, Search, Make,
Run, Options, Browse y Help. Para seleccionar una de las opciones pre-
sentadas, se puede optar por cualquiera de las dos formas siguientes:
• Pulsar la tecla Alt, para activar el menu principal, y despues la te-
cla correspondiente a la letra que se presenta en alto brillo 0 color
diferente (es indiferente el utilizar mayusculas 0 minusculas). Por
ejemplo F para activar el menu correspondiente a la opcion File.
• Pulsar la tecla AIt, para activar el menu principal, y utilizando las
teclas de movimiento del cursor, elegir la opcion deseada (opcion
que se presentara en video invertido respecto al existente 0 en color
diferente) y pulsar la tecla Enter.
Para seleccionar una orden correspondiente al menu presentado por
una opcion del menu principal, se puede proceder de cualquiera de las dos
formas siguientes:
• Pulsar la tecla correspondiente a la letra que se presenta en alto bri-
110 0 color diferente (es indiferente el utilizar mayusculas 0 minus-
culas). Por ejemplo, si se esta en el menu presentado por la opcion
File y se qui ere salir del PWB, se pulsa la tecla x.
• Moverse a la orden deseada por medio de las teclas de movimiento
del cursor y pulsar Enter.
Algunas ordenes de estos menus tienen escrito a su derecha el nombre
de una tecla 0 combinacion de teclas que realizan la misma operacion.
Por ejemplo la cuarta orden del menu presentado por la opcion Edit es
"Cut Shift + Del". Esto significa que al pulsar las teclas Shift + Del se
ejecuta la orden Cut.
Algunas ordenes abren una ventana de dialogo; por ejemplo, la orden
"Find ..." del menu presentado por la opcion Search. En este caso respon-
deremos alas cuestiones planteadas, y finalizaremos pulsando a continua-
cion Enter « OK ».
Siempre que se desee abandonar un menu, se pulsara la tecla Esc
(<Cancel> ).
Para obtener ayuda sobre cualquier orden, seleccionarla y pulsar Fl.
Para abandonar la pantalla de ayuda, pulsar la tecla Esc.
PWB esta disefiado para utilizar un raton de Microsoft 0 uno compatible
con este.
1. Se apunta a la opcion deseada del menu y se pulsa el boton iz-
quierdo del raton.
Para enrollar/desenrollar el texto sobre la ventana activa, se dispone
de una barra de scroll vertical y otra de scroll horizontal. Ambas tienen
en sus extremos un as flechas que indican el desplazamiento imaginario de
la pantalla sobre el texto cuando se efectua la accion de scroll y un cursor
que se desplaza a 10 largo de la linea de scroll, que indica la posicion relati-
va del cursor de pantalla con respecto a los limites del texto.
1. Para avanzar 0 retroceder una linea, se apunta a la flecha supe-
rior 0 inferior de la barra de scroll vertical y se pulsa el boton iz-
quierdo del raton. Para desplazar el texto una posicion a la izquierda
o a la derecha se apunta a la flecha derecha 0 izquierda de la ba-
rra de scroll horizontal y se pulsa el boton izquierdo del raton.
2. Para avanzar 0 retroceder una pagina, se coloca el cursor del ra-
t6n sobre la linea de scroll vertical, entre el cursor de scroll y la
flecha inferior 0 entre el cursor de scroll y la flecha superior, y
se pulsa el bot6n izquierdo del rat6n. La operaci6n es analoga para
desplazar el texto una pagina hacia la izquierda 0 hacia la dere-
cha; eso sf, actuando sobre la barra de scroll horizontal.
3. Si se apunta al cursor de scroll vertical de la ventana de texto y
se tira, con el bot6n izquierdo del rat6n pulsado, hacia arriba 0
hacia abajo arrastrandolo sobre la linea de scroll, el texto se des-
plaza hacia abajo 0 hacia arriba. La acci6n se ve cuando se deja
de pulsar el bot6n. Esta misma operaci6n se puede realizar sobre
la linea de scroll horizontal para desplazar el texto hacia la izquieraa
o hacia la derecha.
Es posible variar el tamafio de una ventana. Para ello, se apunta a la
linea de separaci6n entre ventanas y con el bot6n izquierdo del rat6n pul-
sado, se tira en la direcci6n apropiada para agrandar 0 reducir la ventana.
Para activar una ventana, apuntar a cualquier lugar dentro de la mis-
ma y pulsar el bot6n izquierdo del rat6n.
Para activar 0 desactivar cualquier acci6n dentro de una ventana de
dialogo, apuntar al espacio entre corchetes 0 entre angulos y pulsar el bo-
t6n izquierdo del rat6n.
Al ejecutar las 6rdenes seguidas por tres puntos (...) de los menus citados
anteriormente, se presenta una ventana denominada ventana de dhilogo,
que puede contener cuestiones para responder y opciones para elegir. Para
pasar de una cuesti6n u opci6n a otra, pulsar la tecla Tab. Una vez situa-
dos, podremos elegir 0 eliminar una opci6n con las teclas de movimiento
del cursor. Tambien, y mas faci!, pulsando la tecla correspondiente a la le-
tra en alto brillo de cualquiera de las opciones, activamos 0 desactivamos
dicha opci6n, 0 bien, nos situamos en la correspondiente cuesti6n a res-
ponder.
Desde este menu, entre otras cosas, se pueden crear nuevos ficheros, car-
gar ficheros ya existentes, fusionar ficheros, salvar ficheros, visualizar el
siguiente fichero en la lista, renombrar un fichero, escribir todo 0 parte de
un fichero 0 salir al DOS.
Search Make Run Options
,---------------,- c' ,Cf,m"S
~
Open .
Merge .
Next
Save
Save As ...
Save All
Close
Print ...
DOS Shell Shift+F9
{53.1. 41.8. 19.5. 13.7. 10.8. 20.o};
S] =
...Greeia .....Tunieia .....Turqul.a.....Otros ..};
] = {O. 1. O. O. O. 0 };~xi t I'llt+F4
1 PROG03.C All+l
2 PROG02.C Alt+Z
3 <UNTITLED> Alt+3
~ ~I
pen a New EMptlj File C N 00001.001
/M Modo grafieo de MaS alta resoluei6n M/
if (!_setvideoMode(_MAXRESMODE»
exit( 1 ); /M Grafieos no disponibles M/
Crea un nuevo fichero denominado UNTITLED en memoria. Cuando sal-
vemos el fichero en el disco, prodremos asignarle un nuevo nombre.
Carga en memoria un fichero existente en el disco. El fichero puede estar
en el directorio actual de trabajo, 0 en otro directorio.
Para especificar el fichero que se desea cargar, se puede proceder de
cualquiera de las formas siguientes:
Make Run Options- Brow
C·,Cb00,SOURCE,PROG01.[
1. Escribir e1nombre del fichero a continuacion de File Name y pul-
sar Enter.
2. Pulsar la tecla Tab para situar el cursor sobre la lista de ficheros
(File List) y elegir el deseado, bien con las teclas de movimiento
del cursor, 0 bien pulsando la inicial del nombre una 0 mas veces,
ya que puede haber iniciales repetidas. A continuacion pulsar Enter.
3. Apuntar con el raton al nombre del fichero y pulsar una vez el
boton izquierdo del raton para seleccionarlo 0 dos vcces consecu-
tivas para cargarlo.
Para cambiar de directorio, seleccionar uno de los de la lista Drives
/ Dirs: (aparecen en letras mayusculas) y pulsar Enter; 0 bien, escribir a
continuacion de la pregunta File Name: el camino del directorio al que se
desea cambiar y pulsar Enter.
( 0/ > <Cancel> ( Help >0
• ~I
Fl=Help Enter E<.c C,lnn>! T.b Ne,t FIPlrl [" N 00001.001
,.><
if (! setvideoMode( MAXRESMODE»
exit( 1 ); - ,.>< Graficos no disponibles ><,.
Inserta el contenido de otro fichero, encima de la linea sobre la que se en-
cuentra el cursor.
Cargar el siguiente fichero de la lista de ficheros, 0 historia, visualizada
al final del menu File. El fichero en memoria se descarga de la misma y
su nombre se afiade a esta lista.
Escribe el contenido del modulo que actualmente reside en memoria, en
un fichero en el disco.
Escribe en el disco el contenido del modulo que actualmente reside en me-
moria, con el nuevo nombre especificado.
Cierra el fichero actual mente en memoria y 10 elimina de la lista 0 historia
de ficheros. Si el fichero nunca ha sido salvado 0 ha sido modificado, se
nos preguntara si queremos salvarlo.
Permite escribir por la impresora, todo el contenido del fichero que se esta
editando, 0 solamente el texto seleccionado.
Uinclude <conio.h>
uinclude <stdlib.h>
Uinclude <graph.h>
uinclude <string.h>
lIinclude
udefine
float f
char _fa
{
short _f
< OK ) <Cancel> < Help>
. ~
f1=Hfdp Enter E,;c=Canccl Tab=Next field C 0000l.mll
/N Modo grafico de MaS alta resoluci6n N/
if (! setvideoMode( MAXRESMODE»
exit( 1 ); - /N Graficos no disponibles N/
Permite salir temporalmente al DOS, pudiendo asi ejecutar cualquier otra
tarea bajo el sistema operativo. La vuelta al PWB se hace ejecutando la
orden Exit. Si el fichero actual ha sido modificado es automaticamente
salvado.
Finaliza la sesi6n con Microsoft C (PWB) y nos devuelve al DOS. Si al eje-
cutar esta orden el fichero actual ha sido modificado, es automaticamente
salvado.
Las caracteristicas fundamentales del editor perteneciente al entorno de pro-
gramaci6n, son las siguientes:
• Movimiento del cursor a cualquier parte de la pantalla, para modi-
ficar 0 insertar texto.
• Manipulaci6n de bloques; esto es, mover, duplicar 0 borrar un blo-
que de texto.
Muchas de las 6rdenes que se exponen a continuaci6n requieren la selec-
ci6n previa de un conjunto de caracteres, palabras 0 lineas. Para realizar
esta operaci6n dentro de la ventana activa, colocar el cursor al principio
del texto a seleccionar y manteniendo pulsada la tecla Shift, marcar el tex-
to desplazando el cursor con las teclas de movimiento.
Tambien es posible seleccionar texto con el rat6n. Para ello, proceder
de la forma siguiente:
Apuntar al canicter deseado y con el bot6n iz-
quierdo del rat6n puisado, tirar hacia la dere-
cha (0 izquierda) para seleccionar el canicter 0
caracteres deseados.
Afmntar a la palabra y pulsar dos veces conse-
cutivas el bot6n izquierdo del rat6n.
Apuntar a la primera columna de la linea y con
el bot6n izquierdo pulsado tirar hacia abajo has-
ta seleccionar las lineas deseadas.
EI editor utiliza varias ordenes para realizar las operaciones antes descri-
tas. Estas ordenes se pueden agrupar de la forma siguiente:
Ctrl+S 0
Ctrl+D 0
Ctrl+A 0
Ctrl+F 0
Ctrl+E 0
Ctrl+X 0
Ctrl+
Ctrl+
t
!
Home
End
Ctrl+Home
Ctrl+End
Un canicter a la izquierda
Un canicter a la derecha
Al principio de la palabra a la izda.
Al principio de la palabra a la dcha.
Una linea hacia arriba
Una linea hacia abajo
Al primer nivel de dentacion
Al final de la linea actual
A la primera linea del programa
A la ultima linea del programa
Scroll
Ctrl+W 0 1
Ctrl+Z 0 I
Ctrl+R 0 PgUp
Ctrl+C 0 PgDn
Insertar
Scroll una linea hacia arriba
Scroll una linea hacia abajo
Scroll una pagina hacia arriba
Scroll una pagina hacia abajo
Ctrl+V 0 Ins
End Enter
Ctrl + N Home Enter
Shift + Ins
Ctrl + P + caracter
Activar/desactivar inserci6n
Linea debajo de la actual
Linea encima de la actual
Contenido de la memoria intermedia
Introduce el caracter equivalente a
Acaracter
Ctrl+G 0 Bksp (-)
Del
El caracter a la izda. del cursor
El caracter bajo el cursor
La linea actual, salvandola en la memo-
ria intermedia
El texto seleccionado, salvandolo en la
memoria intermedia
Shift + El caracter a la izda.
Shift + El caracter a la dcha.
Shift + La linea de encima
Shift + I La linea actual
Shift + Ctrl + La palabra a la izda.
Shift + Ctrl + La palabra a la dcha.
Shift + PgUp La pantalla por encima del cursor
Shift + PgDn La pantalla por debaj 0 del cursor
Shift + Ctrl + Home Hasta el principio del programa
Shift + Ctrl + End Hasta el final del programa
FI Ayuda
F2 Cargar ultimo fichero
F3 Repetir la busqueda hacia adelante
F4 Repetir la busqueda hacia atrcis
F6 Siguiente ventana
F7 Ejecutar hasta la posicion actual del cursor
F8 Ejecucion paso a paso, incluyendo funciones de usuario
F9 Poner/quitar punto de parada
FlO Ejecucion paso a paso, excepto para las funciones de usuario
Cuando se selecciona la opcion Edit del menu principal para su ejecucion,
se presenta en pantalla un menu con las siguientes ordenes:
- Edit I.
I
Cut ~hIft·DP!
COPlj I:trl-In',
P,,<:t P. <;h 1 ft· In',
Cl p.le fl,'1
Set Anchor
Selecl To Anchor
. "
Unclo the 11::l co III ny r Or1r11rln IY.( lltin N UUUUl WJl
Esta orden se ejecuta despues de Undo. Cuando se ejecuta Redo el fichero
vuelve a la forma que tenia antes de ejecutar la ultima orden Undo.
Esta orden borra el texto seleccionado y 10 salva en una memoria interme-
dia (ver 6rdenes para seleccionar texto).
Copia en la ventana activa el texto almacenado en la memoria intermedia.
Podemos realizar las dos operaciones siguientes:
Reemplazar texto: seleccionar el texto que deseamos reemplazar en la
ventana activa y ejecutar Paste.
Insertar texto: mover el cursor a la posici6n deseada dentro de la ven-
tana activa y ejecutar Paste. El textose inserta a continuaci6n del cursor.
Esta orden borra el texto se1eccionado y no 10 salva en la memoria in-
termedia.
Salva la posicion actual del cursor como una marca (similar a la orden AKB
de WordStar). Mas tarde, esta marca puede utilizarse como punto final en
la seleccion de texto.
Selecciona el texto que hay entre la posicion actual del cursor y la marca
salvada por la orden Set Anchor.
Permite alternar entre los modos: caja, linea y flujo. En modo caja, se puede
seleccionar una region rectangular de texto. En modo linea, se pueden se-
leccionar solo lineas. En modo flujo, la seleccion se hace siguiendo el texto
ASCII.
Coloca el editor en modo de lectura solamente con 10 que el texto queda
pro~egido contra posibles cambios.
Permite definir el nombre para una macro y las teclas para referirse a ella.
Para asignar las teclas, mover el cursor a [ J y pulsar la tecla 0 combinaci6n
de teclas deseada.
Ejecutar Record On para empezar a registrar una macro, y al finalizar el
registro de la misma.
Permite cambiar una macro existente. Para modificar una macro que aca-
bamos de registrar:
1. Cargar el pseudofichero que contiene la macro, ejecutando la or-
den Edit Macro.
Bloques de texto pueden ser movidos 0 copiados con las 6rdenes Cut, Copy
y Paste. Para ello seguir los pasos que se indican a continuaci6n:
2. Para realizar la operaci6n de mover, ejecutar la orden Cut del menu
Edit.
Para realizar la operacion de copiar, ejecutar la orden Copy del
menu Edit.
En ambos casos, el texto seleccionado es salvado en una memoria
intermedia.
3. Mover el cursor allugar donde se quiere insertar el texto. Este lu-
gar puede estar en:
• otro fichero. Utilizar la orden Open para cargar el fichero en
memoria 0 seleccionarlo de la historia.
Cuando se elige la opcion View del menu principal se presenta un menu
con las siguientes opciones:
Search Make Run Optiqns
r---------------,EO'
~
Spl it Vertical
Size Window'
MaxiMize Window
Close Window
Ctrl +F8
Ctrl+F10
Ctrl+F4'
COMpil e Resul ts
~ ~
'lplll Winnow llo('lzonLlllq dt (IIr·~.nr' '" PlllllI N WllHll rHlt
Divid~ la ventana activa de izquierda a derecha. La division se hace por
la posicion del cursor. EI minimo tamafio es de 5 lineas.
Divide la ventana activa de arriba a abajo. La division se hace por la posi-
cion del cursor. EI minima tamafio es de 10 columnas.
Permite agrandar 0 acortar la ventana activa. Para utilizar esta orden debe
haber al menos dos ventanas abiertas.
Cierra la ventana activa. Para utilizar esta orden debe haber al menos dos
ventanas abiertas.
Abre y cierra la ventana de errores durante la compilacion. Para localizar
la linea del program a donde se ha producido un error:
2. pulsar Shift + F3 para moverse al siguiente error 0 Shift +F4 para
moverse al error anterior. EI cursor se situani sobre la linea del
programa que causo el error.
Este menu per mite encontrar en un programa cualquier texto especificado
y opcionalmente reemplazarlo por otro texto. Consta de las siguientes
6rdenes:
~
Se Iected Text f3
Repeat Last flnd f3
Change .
for FIle .
Next Error Shlft·f3
PrevIous Error Shlft+F4
Set Error
Go To Mark .
DefIne Mark .
Set Mark fIle .
• .1
f Incl c;lCllly UI' l'p~Juld(~ P"PI·f'~.~iun f'<".f-'udo N nnnOl.nnl
Permite buscar un canicter, una palabra 0 un grupo de palabras. Cuando
esta orden se ejecuta aparece una pantalla de dialogo. Para encontrar un
texto determinado, realizar los siguientes pasos:
1. Introducir (seleccionandolo 0 escribiendolo) el texto a buscar a
continuaci6n de la pregunta Find What:.
C Tener en cuenta mayusculas y minusculas.
R Expresiones regulares (busqueda por patrones).
W Busca desde el cursor hasta el final del fichero y desde el final
del fichero hasta el cursor.
o Hacia adelante (Forward).
B Hacia atnis (Backward).
A Pone en alto brillo todas las ocurrencias encontradas. Si no
se selecciona se para en la primera ocurrencia. (Find All).
Cuando se requiere utilizar expresiones regulares, pueden utilizarse
como como dines los siguientes caracteres:
la localizaci6n del texto escrito despues de este caracter debe darse
al principio de una linea.
la localizaci6n del texto escrito antes de este caracter debe darse al
final de una linea.
localizar uno de los caracteres del conjunto especificado entre cor-
chetes. Dentro de los corchetes pueden utilizarse los caracteres espe-
ciales:
para indicar los limites del conjunto de caracteres que se desea
especificar.
 cualquier caracter de los indicados precedido por este caracter, pier-
de su significado especial y es considerado como un caracter normal.
Esta orden permite buscar un texto seleccionado previamente. Los pasos
a seguir son los siguientes:
1. Seleccionar el texto que se quiere buscar (ver 6rdenes del editor
para seleccionar). El texto seleccionado debe de estar sobre una
unica linea.
Tanto utilizando Find como Selected Text se puede repetir la ultima
busqueda, pulsando F3 0 ejecutando la orden Repeat Last Find de este mis-
mo menu.
Esta orden permite repetir la ultima busqueda realizada por Find 0 por
Selected Text. La busqueda puede realizarse hacia adelante (F3) 0 hacia
atnis (F4).
1. Introducir (selecciomindolo 0 escribiendolo) el texto a buscar a
continuaci6n de la pregunta Find What:.
2. Introducir el texto que va a sustituir al anterior a continuaci6n de
la pregunta Change to:.
C Tener en cuenta mayusculas y minusculas.
R Expresiones regulares (busqueda por patrones).
W Busca desde el cursor hasta el final del fichero y desde el
final del fichero hasta el cursor.
< Find and Verify>. Confirmar cada cambio.
<Change All>. Efectuar todos los cambios sin confirmar.
<Cancel>. Cancelar la orden.
"include (c
lIinclude ,
lIinclude 'g
lIinclude ,
lIinclude <
lIdefine PAl
float far
char far
- {"It
short _far explotar[PAISESl
~M Modo grafico de Mas alta resolucibn M~
if (! setvideoMode( MAXRESMODE»
exit( 1 ); - ~M Graficos nO disponibles M~
~ 4
F1=Help Enter Esc=C"ncel T~b=Next FIeld C 00001.001
Encontrar un fichero sobre el disco. La busqueda puede realizarse sobre
un directorio especificado 0 a 10 largo de la estructura en arbol de los di-
rectorios y partiendo de uno determinado.
Mueve el cursor a la linea fuente que contiene el siguiente error que se ha
producido en la compilaci6n.
Mueve el cursor a la linea fuente que contiene el error inmediatamente an-
terior al error de compilaci6n que actual mente se esta visualizando.
Selecciona como error actual el error que esta bajo el cursor. Esta orden
sincroniza las ventanas fuente y la de errores, para que la linea fuente que
contiene el error aparezca en la ventana activa. La orden Set Error esta
disponible si el cursor esta en la ventana de errores de compilaci6n. En otro
caso, no tiene efecto.
Mueve el cursor a una marca definida con la orden Define Mark. Una marca
es un nombre asociado con una posici6n (fila y columna) en un fichero.
Hay dos formas para afiadir el contenido total 0 parcial de otros ficheros
al fichero actual que se esta editando.
Para copiar un fichero entero dentro del texto actual, se utiliza la orden
Merge del menu File. El nuevo texto se inserta encima de la linea sobre la
que esta el cursor.
Para copiar parte de un fichero dentro del texto actual, realizar los siguien-
tes pasos:
1. Ejecutar la orden Open ... del menu File para cargar el fichero que
contiene el texto que queremos copiar, 0 recuperar el fichero de
la historia.
3. Ejecutar una de las 6rdenes Cut 0 Copy del menu Edit, para sal-
var el texto seleccionado en la memoria intermedia.
4. Ejecutar la orden Open ... del menu File para cargar el fichero en
el cual queremos copiar el texto seleccionado, 0 recuperar el fi-
chero de la historia y situar el cursor en el lugar adecuado.
5. Ejecutar la orden Paste del menu Edit. El texto se inserta a conti-
nuaci6n del cursor.
Copiando texto correspondiente al manual de ayuda
electr6nico
4. Pasar a la ventana de edici6n y depositar el texto copiado en el
lugar deseado utilizando la orden Paste.
Un programa C consiste de uno 0 mas ficheros fuente conocidos como
modulos.
Los programas de un unico m6dulo son los mas faciles de crear. Los
pasos a seguir para esto son los siguientes:
3. Compilar, ejecutar y depurar el programa a traves de los menus
Run y Options.
Para mantener un programa farmado por varios m6dulos, se puede
crear una Iista con los nombres de los mismos, mediante la orden Set Pro-
gram List ... del menu Make, y a continuaci6n se procede a la compilacion
de esta lista. Esta orden se estudia a continuaci6n. Esta lista sera utilizada
par Microsoft C para reconstruir el programa, cuando cualquiera de sus
m6dulos haya sido modificado. Otra posible soluci6n, aunque no tan co-
moda, seria construir una libreria, cuesti6n que fue vista en el capitulo co-
rrespondiente a "Librerias y utilidades del compilador".
Cuando se dige la opci6n Make del menu principal, se presenta un menu
con las siguientes opciones:
uinclude <conio.h>
Uinclude <stdlib.h>
Uinclude <graph.h>
Uinclude <string.h>
Uinclude <pgchart.h>
Set PrograM LIst ...
Edit PrograM Llst... PROG
Clear PrograM Ll~t
Udefine PAlSES &
float _far valor[PAISESl = {53.1. 41.8. 19.5. 13.7. 1a.8. za.&};
char far HeleMentos[PAISESl =
- {..Italia .....Espana .....Grecia .....Tunicia .....l'urquia..•..Otros"};
short _far expiotar[PAISESJ = {a. 1. a. a. a. a };
/H Modo grafico de Mas alta resoluci6n H/
if (! setvideoMode( MAXRESMODE»
eXit( 1 ); - /H Graficos no disponibles */
~ ~
COMpIle current source fIle C 00003.059
Esta orden crea un fichero objeto (.obj) del m6dulo actual, pero no efec-
tua e1enlace (link).
Esta orden compila y enlaza todos los m6dulos pertenecientes al programa
actual que hayan sido modificados desde la ultima vez que se compilaron
y enlazaron, y crea un unico fichero ejecutable (.exe). Los programas for-
mados por varios m6dulos requieren de una lista (program list).
Esta orden compila y enlaza todos los modulos pertenecientes al programa
actual (program list), creando un unico fichero ejecutable (.exe).
Esta orden crea una nueva lista de modulos para un programa 0 carga una
lista ya existente.
Antes de proceder como se indica a continuacion, elegir el tipo de fi-
chero que se desea construir. Para ello ejecutar la orden Build Options del
menu Options. De la ventana de dialogo presentada, elegir la opcion Set
Initial Build Options, la cual nos permitira elegir el tipo de fichero que
deseamos construir; por ejemplo, DOS EXE.
( OK ) <Cancel> < Help>
Para crear un programa con multiples modulos, seguir los pasos que
se indican a continuacion:
1. Crear los ficheros fuente que van a formar el programa, siguien-
do para cada uno de ellos los puntos que a continuacion se indican:
a) Ejecutar la orden New del menu presentado par File para crear
un nuevo fichero fuente, 0 ejecutar la orden Open de este mis-
mo menu para editar un fichero que ya existe.
2. Ejecutar la orden Set Program List del menu presentado por File,
para crear la lista del programa 0 para cargar una ya existente.
3. Ejecutar la orden Edit Program List del mismo menu, para afia-
dir y/o eliminar modulos de la lista (Add/Delete) y salvar la mis-
ma (SaveList). Es posible tambien, afiadir ficheros objeto y libre-
rias .LIB a la lista.
5. Finalmente, compilar, ejecutar y depurar el programa formado por
esa lista de modulos.
Cuando se compila un programa farmado por multiples modulos, Mi-
crosoft C verifica la lista del programa para ver si algun modulo ha cam-
biado desde la ultima vez que el programa fue compilado. Si es as!, Micro-
soft C vuelve a compilar los modulos que han cambiado antes de reconstruir
el programa.
La lista del programa es guardada en un fichero en el disco, que tiene
el mismo nombre del programa y extension .mak. Fuera del entorno de Mi-
crosoft C (PWB), el fichero .mak sera la entrada para la utilidad NMAKE.
Esta arden permite mantener la lista de modulos que componen un
programa.
lista de 10s ficheros fuente existentes en e1 directorio
especificado.
esta opdon afiade e1fichero se1eccionado a 1a lista del
programa 0 10 elimina de 1a lista. Si e1fichero no esta
en 1a lista 10 afiade y si esta 10 e1imina.
salva todos 10s cambios efectuados en 1a lista de mo-
du10s que componen e1 programa en un fichero con
extension .mak.
Cuando se elige la opci6n Run del menu principal, se presenta un menu
con las siguientes opciones:
Run DOS C ...J!'l'uf,d ...
[U=.tC'11=E r-"lenu..•
. .
[,ecule the current pr0gr"~ p~~IJ~- N OOOQl.nQl
Permite introducir argumentos para ser interpretados por el programa cuan-
do se ejecute la orden Ejecute 0 la orden Debug. Esta orden es equivalente
a introducir los argumentos en la linea de 6rdenes de DOS.
Permite ejecutar cualquier orden DOS sin salir del PWB. No ejecutar pro-
gramas residentes (TSR), tal como MSHERC.COM.
Permite afiadir hasta seis 6rdenes al menu Run. Esta orden nos permite
ejecutar un programa desde dentro de PWB sin tener que especificar el nom-
bre del programa y sus argumentos cada vez.
La orden Customize Menu presenta un menu con las siguientes op-
dones:
Options • •
Iiiiiiiit • - •
Rullel Option,; ...
Hr/)w~.•e Optiuns ...
C LUMpllcr Optlun~ ...
LINK OptIons .
NMAKE OptIonS .
CodeUlew OptIonS ...
. ~
~et envIronMent optIons pseuelo N 00001.001
Define los directorios donde buscar los ficheros de cabecera (.h), las libre-
rias (.lib) y los ficheros de ayuda (.hlp) de Microsoft C. Estos caminos de
busqueda normalmentte son definidos utilizando variables de entorno del
DOS.
Permite asignar teclas que ejecutan 6rdenes, macros y funciones. Esta in-
formaci6n es almacenada en un pseudofichero (area de memoria que nun-
ca es escrita sobre el disco).
Permite realizar cambios en el pseudofichero que contiene el conjunto ac-
tual de asignaciones hechas para el editor. Ejecutar la orden Save, para sal-
var los cambios efectuados en el fichero TOOLS.IN!.
Utilizar esta orden para: indicar si el programa va a ser depurado (Debug)
o no (Release); seleccionar el directorio destino de los ficheros resultantes
de la construccion del programa; seleccionar el lenguaje fuente; seleccio-
nar opciones predefinidas para la construccion del programa; salvar el con-
junto de opciones una vez actualizadas, para utilizarlas mas tarde.
Permite utilizar una base de datos previamente construida, para analizar
el codigo fuente de un programa.
1. Generar una lista de modulos utilizando la orden Set Program List
del menu Make. Estos son los ficheros que formaran la base de
datos.
2. Ejecutar la orden Browse Options del menu Browse y seleccionar
la opcion Generate Browse Information. El resto de los campos
son opcionales.
3. Construir el programa utilizando las ordenes del menu Make. Este
proceso crea un fichero con extension .BSC (Browser Source Ca-
che) que contiene informacion para el analizador (Browser).
Una vez completado estos pasos, tenemos construida la base de da-
tos; por 10 tanto, podemos pasar a utilizar las ordenes del menu Browse
(ver menu Browse).
Esta orden permite fijar opciones para compilar un programa C. Estas op-
ciones son:
Numero de segmentos
C6digo Datos
Small
Medium
Compact
Large
Huge
1
1
varios
varios
varios
(arrays> 64K)
varios
varios
Niveles de advertencia 0, 1, 2, 3 y 4. Cuanto mayor
es el nivel, mayor es la informaci6n que nos da el com-
pilador.
Especifica las librerias bajo las cuales estamos tra-
bajando.
DOS
Windows
Windows DLL
Utiliza por defecto las librerias
apropiadas.
Librerias para el modo pro-
tegido
Librerias para el modo real
Librerias para windows
Para trabajar con librerias para
windows enlazadas dimimi-
camente
Para trabajar con librerias para
OS/2 enlazadas dinamicamente
libreria para crear una aplica-
cion que utiliza multithreads.
Deshabilita las extensiones que
Microsoft afiade al lenguaje.
Permite utilizar las extensiones
de Microsoft.
Habilita el conjunto de instrucciones del procesador
especificado.
8086
80186
80286
/00
/01
/02
Convenio de Hamada
Determina el convenio de Hamada a funciones.
Fastcall
Pascal
C
/Or
/Oc
/Od
Windows Entry/Exit Code
Oenera c6digo para Windows (Ow).
Additional Options...
Afiadir opciones que no estan en esta pantalla.
Set Debug Options...
Afiadir opciones cuando un programa se compila para
depurarlo.
Show Debug Options...
Mostrar las opciones utilizadas por defecto, cuando
un program a se compila para depurarlo.
Set Release Options...
Afiadir opciones cuando un programa se compila nor-
malmente.
Show Release Options...
Mostrar las opciones utilizadas por defecto, cuando
un programa se compila normalmente.
Las 6rdenes de este menu permiten analizar un programa, a traves de sus
declaraciones y funciones.
ninclude <conio.h>
ninclude <stdlib.h>
ninclude <graph.h>
ninclude <string.h>
ninclude <pgchart.h>
. . I, .l~olo h'eference.
U ip.IJ h'el::"ti()n~-:hip.
Li"t. Hpfprpncl-'~;.
[all Tree.
,
Outline ..
Next [trl·NllMo
Previou::::; Cll~l'Nun
.[""", S~n~;i l i Vp
Spl it. WiIldow~.;
ndefine PAISES D
float far valor[PAISES] = {53.1. 41.8. 19
char far MeleMBntos[PAISES] =
- {..Italia .....Espaila.....Grecia .....Tunici
short _far explotar[PAISESJ = {B. 1. B,
+-- ~ tl
'nd deflnll.l0n(sl of SLJMbol [ 00004.0/4
~M Modo grafico de MdS alta resoluci6n M/
if (! setvideoMode( MAXHESMODE»
exit( 1 ); - /M Graficos no disponibles M/
Visualiza una lista de ficheros y numeros de linea donde se ha echo refe-
rencia a los simbolos utilizados en el programa. Con esta orden podemos
encontrar la referencia para cualquier funci6n, variable, tipo 0 macro.
Esta orden provee informaci6n detallada a cerca de varias porciones del
programa. Permite examinar funciones, variables, tipos y macros.
Utilizar esta orden para ver con respecto a cada funci6n, que funciones
llama y que variables, tipos y macros utiliza.
Presenta una estructura en arbol, la cual permite ver que funciones llaman
otras funciones.
Visualiza un fichero que no este en linea. Si introducimos un *.*, entonces
se visualizan todos los ficheros.
Cuando se selecciona esta opcion, aparecen dos ventanas: una para el Brow-
ser y otra para la ventana activa.
EI menu de ayuda aparece cuando se pulsan las teclas AU + H. En el apare-
cen las clases de ayuda, que a continuacion describimos.
/H Modo 9rafico de MaS alta resoluci6n H/
if (! setvideOMode( MAXRESMODE»
exit( 1 ); - /H Grilficos
~r.onteots Shlft"FI
TopIc' ch~rtenv FI
Help on Help
Nex t· Chll rlenv
-P9_defaultchart(£ent. _PG_BARCHART. _PG_PLAINBARS);
strcpy(ent.Maintitle.title. "Producci6n de aceite de oliva");
-P9_chart(£ent. eleMentos. valor. PAISES);
getch( );
_clearscreen(_GCLEARSCREEN);
-P9_defaultchart(£ent. _PG_COLUMNCHART. PG PLAINBARS);
strcpy(ent.Maintitle.title. "Producci6n de aceite de oliva");
-P9_chart(£ent. eleMentos. valor. PAISES);
~ ~
l)lew lnriex of hplp tOPICS r. 00017.0or,
Para obtener informacion a cerca de una orden de un menu cualquie-
ra, utilizar las teclas del cursor para elegirla y pulsar Fl 0 apuntarla con
el raton y pulsar el boton derecho.
Para obtener informacion a cerca de una palabra clave, un operador
o una funcion, poner el cursor sobre dicho elemento y pulsar Fl 0 apun-
tarla con el raton y pulsar el boton derecho.
Indice alfabetico de todas las palabras reservadas utilizadas por el com pi-
lad or C (palabras clave, funciones, tipos, etc.). Para utilizarlo:
2. Elegir la palabra clave deseada, moviendo el cursor allugar de la
misma, utilizando las teclas de movimiento del cursor PgDn y
PgUp.
1. Elegir la materia deseada moviendo el cursor al lugar donde se
indica.
Nos proporciona ayuda sobre cualquier palabra clave C. Esta palabra pue-
de pertenecer al program a actual 0 al fichero de ayuda de Microsoft C.
Para obtener esta ayuda:
Lenguaje c
• Un ordenador IBM PC 0 compatible con un sistema operativo DOS
version 3.0 0 superior, 0 OS/2 version 1.1 0 superior.
• Un procesador 8088 minima 8 Mhz. Se recomienda un procesador
80286.
• 384K de memoria extendida, si tenemos que depurar programas
largos.
• Una unidad de disco duro con un minima de 8 Mb disponibles y
una unidad de disco flexible.
3. SETUP crea 10s ficheros NEW-VARS.BAT, NEW-CONF.SYS y
TOOLS.PRE. E1 contenido del fichero NEW-CONF.SYS debe ser
puesto en e1 fichero CONFIG.SYS. Las 6rdenes contenidas en el
fichero NEW-VARS.BAT tienen que ejecutarse antes de arrancar
Microsoft C; si 10 desea puede incluirlas en e1 fichero AUTOE-
XEC.BAT. Renombrar TOOLS.PRE por TOOLS.INI.
Si durante el proceso de instalaci6n se equivoca, interrumpa dicho pro-
ceso pulsando las teclas Ctrl +C y ejecute de nuevo el programa SE-
TUP.EXE. Este nunca borra ficheros de los discos del paquete Microsoft C.
Para instalar Microsoft C inserte el disco SETUP en la unidad A, cambie
a la unidad A (A: <Enter» y emita la orden:
esta opci6n es uti! cuando una vez instalado Microsoft C
necesitamos afiadir mas librerias combinadas. Esto evita
el tener que realizar de nuevo la instalaci6n.
esta opci6n suprime la informaci6n de ayuda enviada a la
pantalla durante el proceso de instalaci6n.
Una vez ejecutada la orden SETUP, se nos va preguntando las cues-
tiones necesarias para realizar la instalaci6n. Presentamos a continuaci6n
un ejemplo de una posible instalaci6n.
IMPORTANTE: Si usted no ha leido la informaci6n referente al programa
SETUP localizada en el manual Microsoft C Develoment System "Insta-
lling and Using", por favor hagalo antes de continuar con el proceso de
instalaci6n. Este documento, junto con el fichero README.DOC locali-
zado en el disco SETUP, contiene informaci6n importante.
Si usted no esta seguro de alguna respuesta, haga suya la respuesta
por defecto. Si mas tarde considera que quiere realizar otras elecciones a
las realizadas, ejecute SETUP otra vez.
Usted es preguntado por la unidad de disco desde la cual va a efectuar la
instalaci6n.
Host Operating Mode: OS/2 Protect Mode [N]: N
OS/2 Real Mode and DOS [Y]: Y
El compilador C puede correr bajo OS/2 y bajo DOS. Elija el modo en
el cual va a desarrollar sus programas. Se pueden especificar ambos modos.
Target Operating Mode: OS/2 Protect Mode [N]: N
OS/2 Real Mode and DOS [Y]: Y
El compilador C puede correr bajo OS/2 y bajo DOS. Elija el modo bajo
el cual va a ejecutar sus programas. Se pueden especificar ambos modos.
i,Desea construir librerias combinadas? Para ganar velocidad el compila-
dor C, para cada modelo, junta las librerias individuales en una sola libreria.
Hay tres opciones para soportar las operaciones en punta flotante. El emu-
lador utiliza el coprocesador matematico si esta presente; si no 10 esta, 10
emula mediante software. Esta es la opci6n par defecto. La libreria 80x87
utiliza el coprocesador matematico si esta presente; si no 10 esta, ocurre
un error. La libreria matematica alternativa nunca utiliza coprocesador.
Memory models: Small [Y]: Y Medium [N]: N Compact [N]: N
Large [N]: Y
Microsoft C soporta librerias para seis modelos diferentes de memoria. Usted
puede elegir cualquiera de ellos 0 todos. Se recomienda seleccionar los mo-
delos que utilice mas a menu do.
Cuando SETUP finaliza la construcci6n de las librerias combinadas elegi-
das, las librerias fuente a partir de las cuales se ha realizado la construc-
ci6n, residen aun en el disco. Elias pueden ser borradas al finalizar la ins-
talaci6n para liberar espacio en el disco.
Include in combined libraries: GRAPHICS.LIB [N): Y
PGCHART.LIB[N): Y
l,Incluye en las librerias combinadas las librerias gnificas GRAPHICS.LIB
y PGCHART.LIB? La libreria GRAPHICS.LIB se utiliza para graficos
en general y la libreria PGCHART.LIB se utiliza para presentaciones gia-
ficas (diagramas).
Si usted esta conforme con las respuestas pulse 'N' para continuar. Si quiere
modificar alguna de Ias respuestas pulse 'Y'. Si usted quiere salir pulse 'Q'.
Si se va a utilizar un rat6n (Microsoft Mouse) responder 'Y' para instalar
el fichero MOUSE.COM.
Hay varios ficheros con documentaci6n acerca de esta versi6n como READ-
ME.DOC. Nosotros Ie aconsejamos que Ios copie.
Si usted tiene el sistema operativo de IBM(R) PC-DOS 3.20, puede que
necesite parchear el sistema operativo utilizando estos ficheros, para mani-'
pular errores en coma flotante.
Si usted esta conforme con las respuestas, pulse 'N' para continuar. Si quiere
modificar alguna de las respuestas pulse 'Y'. Si usted quiere salir pulse 'Q'.
Directorio para los ficheros que pueden ejecutarse en ambos modos (real
y protegido).
En esta pregunta y siguientes, si el directorio especificado no existe,
se nos preguntara si deseamos crearlo. Responder "Y".
Directory for Real mode (DOS) executable files [C:  C600  BIN]:
C:  C600  BIN
Si usted esta conforme con las respuestas, pulse 'N' para continuar. Si quiere
modificar alguna de las respuestas pulse 'Y'. Si usted quiere salir pulse 'Q'.
A continuaci6n se nos van pidiendo los discos, para copiar los fiche-
ros que componen el paquete Microsoft C en los directorios especificados.
PARTE
8
Apendices
• Ficheros .h de C
• C6digos de Caracteres (ASCII)
• Indice Alfabetico
A continuacion se detallan 10s ficheros de cabecera (.h) correspondientes
a C y las declaraciones que contienen.
_bios_disk
_bios_equiplist
_bios_keybrd
_bios~emsize
_bios_printer
_bios_serialcom
cgets
cprintf
cputs
cscanf
getch
getche
inp
inpw
isalnum
isalpha
isascii
iscntrl
isdigit
isgraph
islower
isprint
ispunct
chdir
getcwd
kbhit
outp
outpw
putch
isspace
lsupper
isxdigit
_bios_timeofday
int86
int86x
to ascii
tolower
toupper
_tolower
_toupper
_psp _osmajor
_osmode _doserrno
struct WORDREGS
struct BYTEREGS
union REGS
struct SREGS
struct find_t
struct DOSERROR
struct dosdate_t
struct dostime_t
bdos _dos----,getdate
_chain~ntr _dos----,getdiskfree
_disable _dos----,getdrive
_dos_allocmem _dos----,getfileattr
_dos_close _dos----,getftime
_dos_creat _dos~ettime
_dos_creatnew _dos_getvect
_dos_findfirst _dos_keep
_dos_findnext _dos_open
_dos_freemem _dos-fead
_dos_setblock
_dos_setdate
_dos_setdrive
_dos_setfileattr
_dos_setftime
_dos_settime
_dos_setvect
_dos_write
dosexterr
_enable
_harderr
jardresume
_hardretn
int86
int86x
intdos
intdosx
segread
Definicioncompatiblede Microsoftde los modos de lecturay escriturapara
la fundon open.
_arc
_clearscreen
_displaycursor
_ellipse
_floodfill
_getbkcolor
~etcolor
_getcurrentposition
_getfillmask
~etimage
~etlinestyle
~etlogcoord
~etphyscoord
~etpixel
~ettextcolor
~ettextposition
~etvideoconfig
-imagesize
_lineto
_moveto
_outtext
_pie
_putimage
_rectangle
-femapallpalette
-femappalette
_selectpalette
_setacti vepage
_setbkcolor
_setcliprgn
_setcolor
_setfillmask
_setlinestyle
_setlogorg
_setpixel
_settextcolor
_settextposition
_settxtwindow
_setvideomode
_setviewport
_setvisualpage
_wrap on
access dup2 mktemp tell
chmos eof open umask
chsize felelngth read unlik
close isatty rename write
creat locking setmode
dup lseek sopen
alloca
_fmalloc
~free
~heapchk
~heapset
~heapwalk
_fheapwalk
_msize
realloc
sbrk
stackavail
~emmax
_nmsize
_ffree
_fheapchk
_fheapset
~malloc
_expand
free
_freect
halloc
callox
_fmsize
hfree
malloc
_memavl
abs bessels fabs
acos cabs floor
asin ceil fmod
at an cos frexp
atan2 cosh htpot
atof exp labs
ldexp sin
log sinh
loglO sqrt
matherr tan
modf tanh
pow
memccpy
memicmp
memchr
memset
abort
execl
execle
exclp
execlpe
execv
execve
execvp
execvpe
exit
_exit
getpid
spawnl
spawnle
memcmp
movedata
spawnlp
spawnlpe
spawnv
spawnve
spawnvp
spawnvpe
system
bsearch
qsort
getchar
putc
clearerr
putchar
ferror
fileno
getc
feof
Funciones prototipo:
bsearch fgetpos fscanf setvbuf calloc
fgets fseek tempnam fsetpos puts
tmpfile fclose flushall ftell putw
tmpnam fcloseall fopen fwrite qsort
ungetc fdopen fprintf remove vfprintf
fputc rename vprintf fputchar gets
rewind vsprintf fflush fputs getw
rmtemp fgetc fread perror scanf
fgetchar freopen printf setbuf
abort ecvt Idiv perror srand
avs exit -lrot! putenv strtos
atexit _exit -lrotr qsort strtol
atof fcvt Itoa rand strtoul
atoi free ~akepath ralloc swab
atol gcvt f malloc .-rot! system
vsearch tetenv max .-rotr tolower
calloc itoa min _searchenv toupper
div labs onexit _splitpath ultoa
_doserrno
_osminor
sYS--llerr
_osmajor
sys_errlist
_fmode
_psp
errno
_osversion
environ
_osmode
memccpy
memchr
memcmp
memcpy
memicmp
memmove
memset
,movedata
strcat
strchr
strcmp
strcmpi
strcpy
strcspn
strdup
strerror
stricmp
strlen
strlwr
strncat
strncmp
strncpy
strnicmp
strnset
strpbrk
strrchr
strrev
strset
strspn
strstr
strtok
strupr
asctime
clock
_strtime
ctime
_strdate
mktime
tzset
time
localtime
Lenguaje c
CODIGOS DE CARACTERES ( ASCII)
Valor Valor Caracter
Caracter
decimal hexadecimal de control
0 00 NUL Nulo
1 01 SOH Q
2 02 STX
•3 03 ETX 'I
4 04 EOT
•5 05 ENQ +
6 06 ACK +
7 07 BEL Zumbido, Pitido, "bip"
8 08 BS D
9 09 HT Tabulaci6n
10 OA LF Avance de linea
11 OB VT Cursor a posici6n inicial
12 OC FF Avance de pagina
13 OD CR Retorno de carro, introducir
14 OE SO ~
15 OF SI i;1-
16 10 DLE •
17 11 DCl '4
18 12 DC2 I
19 13 DC3 !!
20 14 DC4 1r
21 15 NAK §
22 16 SYN -
23 17 ETB -L
24 18 CAN I
25 19 EM I
26 lA SUB -27 1B ESC -
28 IC FS Cursor a derecha
29 ID OS Cursor a izquierda
30 IE RS Cursor arriba
31 IF US Cursor abajo
Valor Valor
Caracter
decimal hexadecimal
32 20 Espacio
33 21 !
34 22 "
35 23 #
36 24 $
37 25 0,70
38 26 &
39 27
,
40 28 (
41 29 )
42 2A •43 2B +
44 2C ,
45 2D -
46 2E
47 2F /
48 30 0
49 31 1
50 32 2
51 33 3
52 34 4
53 35 5
54 36 6
55 37 7
56 38 8
57 39 9
58 3A :
59 3B ;
60 3C <
61 3D =
62 3E >
63 3F ?
64 40 @
65 41 A
66 42 B
67 43 C
68 44 D
69 45 E
70 46 F
71 47 G
72 48 H
73 49 I
74 4A J
75 4B K
76 4C L
77 4D M
78 4E N
79 4F 0
Valor Valor
Caracter
decimal hexadecimal
80 50 P
81 51 Q
82 52 R
83 53 S
84 54 T
85 55 U
86 56 V
87 57 W
88 58 X
89 59 Y
90 5A Z
91 5B [
92 5C 
93 5D ]
94 5E A
95 5F -
96 60 .
97 61 a
98 62 b
99 63 c
100 64 d
101 65 e
102 66 f
103 67 g
104 68 h
105 69 i
106 6A j
107 6B k
108 6C 1
109 6D m
110 6E n
III 6F 0
112 70 p
113 71 q
114 72 r
115 73 s
116 74 t
117 75 u
118 76 v
119 77 w
120 78 x
121 79 y
122 7A z
123 7B (
124 7C I
I
125 7D I
126 7E -
127 7F Cl
Valor Valor
Caracter
decimal hexadecimal
128 80 c;
129 81 ii
130 82 e
131 83 a
132 84 a
133 85 a
134 86 a
135 87 t;
136 88 e
137 89 e
138 8A e
139 8B i'
140 8C 1
141 8D i
142 8E A
143 8F A
144 90 E
145 91 ae
146 92 AE
147 93 0
148 94 6
149 95 0
150 96 u
151 97 U
152 98 Y
153 99 b
154 9A 0
155 9B t;
156 9C £
157 9D r
158 9E Pt
159 9F f
160 AO a
161 Al i
162 A2 6
163 A3 U
164 A4 il.
165 A5 N
166 A6 a
167 A7 0
168 A8 l,
169 A9
.--170 AA ---,
171 AB Yz
172 AC Y4
173 AD i
174 AE «
175 AF »
Valor Valor
Caracter
deCimal hexadecimal
176 BO
177 Bl
178 B2
......
......
179 B3 I
180 B4
.,
181 B5 ~
182 B6 -;1
183 B7 ""lI
184 B8 .,
185 B9 ~I
186 BA II
187 BB =;)
188 BC =!J
189 BD ~
190 BE ••
191 BF .,
192 CO L
193 Cl ~
194 C2 .,..
195 C3 I-
196 C4 -
197 C5 +
198 C6 F
199 C7 It-
200 C8 t=
201 C9 IF
202 CA ~
203 CB 'iF
204 CC 1:=
205 CD =
206 CE "'-,...
207 CF ••208 DO -"'-
209 D1 "I'
210 D2 ."..
211 D3 lL
212 D4 b.
213 D5 F
214 D6 rr
215 D7 +
216 D8 +
217 D9 .J
218 DA r
219 DB
220 DC •221 DD •
222 DE I
223 DF I
224 EO -ex
Valor Valor
Caracter
decimal hexadecimal
225 El {3
226 E2 r
227 E3 'K
228 E4 E
229 E5 (J
230 E6 p.
231 E7 T
232 E8 41
233 E9 (J
234 EA 0
235 EB [)
236 EC 00
237 ED 0
238 EE ~
239 EF n
240 FO =
241 Fl ±
242 F2 ~
243 F3 s
244 F4 J
245 F5 r
246 F6
247 F7 ""
248 F8 0
249 F9
250 FA
251 FB r252 FC n
253 FD 2
254 FE •255 FF (espacioen
blanco'FF')
3
15
16-25
30-38
44-50
59-68
71
72
73
75
77
79
80
81
82
83
84-93
94-103
104-113
114
115
116
117
118
119
120-131
132
133
134
135
136
137
138
139
140
NUL (null character)
Shift Tab (- < + + )
Alt-Q/W/E/R/T/Y/UlI/O/P
Alt-A/S/D/F/G/H/II J/K/L
Alt-Z/X/C/V/B/N/M
Keys FI-FIO (disabled as softkeys)
Home
Up arrow
PgUp
Left arrow
Right arrow
End
Down arrow
PgDn
Ins
Del
F11-F20 (Shift-Fl to Shift-FIO)
F21-F30 (Ctrl-Fl through FIO)
F31-F4O (Alt-Fl through FIO)
Ctrl-PrtSc
Ctrl-Left arrow
Ctrl-Right arrow
Ctrl-End
Ctrl-PgDn
Ctrl-Home
Alt-l/2/31 4/516/7 1819/01-1 =
Ctrl-PgUp
F11
F12
Shift-F11
Shift-Fl2
Ctrl-F11
Ctrl-Fl2
Alt-F11
Alt-F12
876 ENCICLOPEDIA DEL LENGUAJE C
CODIGOS DEL TECLADO
Tecla Codigo en Hex Tecla Codigo en Hex
Esc OJ Left/Right orrow OF
!l 02 Q 10
@2 03 W 11
#3 04 E 12
$4 05 R 13
%5 06 T 14
A6 07 Y 15
&7 08 U 16
·S 09 I 17
(9 OA 0 18
)0 OB P 19
- OC {f lA
+= OD V 18
Backspace OE Enter lC
Ctrl lD : 2B
A IE Z 2C
S IF X 2D
D 20 C 2E
F 21 V 2F
G 22 B 30
H 23 N 31
J 24 M 32
K 25 <, 33
L 26 >. 34
., 27 ?/ 35
28 RightShift 36
29 PrtScr· 37
LeftShift 2A Aft 38
Spacebar 39 7Home 47
Caps Lock 3A SUp arrow 48
Fl 3B 9PgUp 49
F2 3C 4A
F3 3D 4Left arrow 4B
F4 3E 5 4C
F5 3F 6Right arrow 4D
F6 40 + 4E
F7 41 lEnd 4F
FS 42 2Down arrow 50
F9 43 3PgDn 51
FIO 44 OIns 52
Fl1 D9 Del 53
Fl2 DA
Num Lock 45
Scroll Lock 46
INDICE ALFABETICO
#define 91, 392
#error 400
#if 396
#ifdef e #ifndef 399
#include 91, 395
#line 399
#pragma 400
#undef 395
_arc 758
_asm 620
Definici6n de macros en lenguaje
ensamblador 629
Liamando a funciones C 633
Manipulaci6n de interrupciones 635
Reemplazar una funci6n C 634
Saito a una etiqueta 640
Trabajando con estructuras 637
Trabajando con punteros 635
Utilizando elementos de C en un bloque
Utilizando y salvando registros 630
_asm 626
_based 528
_bcalloc 541
_bfree 541
_bfreeseg 536
_bheapseg 535
_bios_disk 658
_bios_equiplist 660
_bios_keybrd 660
_bios~emsize 661
_bios_printer 662
_bios_serialcom 664
_bios_timeofday 666
_bmalloc 541
_brealloc 541
_chaia-jntr 687
_clearscreen 754
_disable 687
_displaycursor 742
_dos_allocmem 687
_dos_close 687
_dos_creat 688
_dos_creatnew 688
_dos_findfirst 688
_dos_findnext 688
_dos.-freemem 688
_dos~etdate 688
_dos~etdiskfree 689
_dos~etdrive 689
_dos~etfileattr 689
_dos~etftime 689
_dos~ettime 689
_dos~etvect 689
_dos_keep 689
_dos_open 690
_dos-fead 690
_dos_setblock 690
_dos_setdate 690
_dos_setdrive 690
_dos_setfileattr 691
_dos_setftime 691
_dos_settime 691
_dos-setvect 691
_dos_write 691
_ellipse 757
_emit 625
_enable 691
_expand 541
_far 527
_fcalloc 541
_ffree 541
_flood fill 753
_fmalloc 541
_fmemccpy 547
_fmemchr 547
-fmemcmp 547
-fmemcpy 547
_fmemicmp 547
-fmemmove 547
_fmemset 547
_fpreset 705
_frealloc 541
_fsopen 353
~etbkcolor 750
~etcolor 750
~etcurrentposition 760
~etfillmask 752
~etfontinfo 799
~etgtextextent 799
~etimage 764
~etlinestyle 751
~etphyscoord 746
~etpixel 760
~ettextcolor 761
~ettextposition 762
~etvideoconfig 743
~etviewcoord 746
~etwindowcoord 778
-harderr 692
-hardresume 692
-hardretn 692
-huge 528
-.imagesize 764
-.-lineto 755
-fUoveto 755
-Jlcalloc 540
-Jlear 527
_nfree 541
-Jlmalloc 540
-Jlrealloc 541
_outgtext 799
_outtext 762
_p~chart 788
_p~chartms 791
_p~chartpie 789
_p~chartscatter 793
_p~chartscatterms 793
_p~defaultchart 787
_pg-.initchart 787
_pie 758
_putimage 765
-fectangle 756
-fegisterfonts 797
-femapallpalette y -femappalette 748
_selectpalette 749
-setactivepage 744
_setbkcolor 749
_setcliprgn 747
_setcolor 750
_setfillmask 751
_setfont 797
_setgtextvector 800
_setlinestyle 751
_setpixel 760
-settextcolor 761
_settextposition 761
-settextwindow 762
_setvideomode 743
_setvideomoderows 743
_setvieworg 745
_setviewport 747
-setvisualpage 744
_setwindow 775
_unregisterfonts 800
_wrapon 762
abort 701
Abrir un fichero 317, 319, 356, 357
abs 296
Accesibilidadde variables. Ambito 101
Acceso aleatorio 319, 348, 366
Acceso secuencial 319
access 679
acos 293
Algoritmos hash 497
Animaci6n 764
Animaci6n de un objeto 766
Arboles 449
Arboles binarios 450
Borrado en arboles 458
de busqueda 453
perfectamente equilibrados 460
Recorrido de arboles binarios 451
Argumentos en la linea de 6rdenes 274
Arrays 163
de cadenas de caracteres 182
de estructuras 200
de punteros 233
Declaraci6n ge un array 164
dimimicos 242
hash 498
multidimensionales 165
unidimensionales 164
Asignaci6n dinamica de memoria 239, 414
asin 293
asserLh 857
atan 294
atan2294
atexit 702
atof 190
atoi 190
atol 190
bdos 653
BIND 617
bios.h 857
break 138
bsearch 305
Buffer asociado con stdin 176
Busqueda binaria 486
Busqueda de datos 485
Busqueda secuencial 485
Cadenas de caracteres 173
calloc 243
Camino 669
Campos de bits 207
Caracter fin de linea y caracter fin de fichero 123
Caracteres de C 52
Caracteres especiales y signos de puntuaci6n 53
cast 85
ceil 296
Cerrar un fichero 318, 323, 356, 363
cgets 376
char 54
chdir 674
chmod 680
chsize 682
CL 550
Clases de almacenamiento 103
Clasificaci6n de datos 473
clearerr 124, 324
clock 300
close 363
Code View 596
Calls 616
Com pilar y enlazar un programa C para
depurar 597
con rat6n (n:lOuse)603
Edit 605
File 604
Invocando a Code View 597
Menus de Code View 601
Opciones de Code View 598
Options 614
Run 611
Search 609
Seleccionando texto 604
View 606
Watch 612
c6digos de salida 686
Colas 432
Colores en modo grafico utilizando CGA 736
Colores en modo grafico utilizando VGA, MCGA
y EGA 739
Colores en modo texto 734
Comentarios 70
Comenzar' un nuevo proceso 712
compact 519
Compilaci6n 47
Compilaci6n condicional 396
Compilar y ejecutar el prograrna 44
conio.h 858
const 72
Constante de un solo caracter 67
Constantes 64
Constantes de caracteres 67
Constantes enteras 65
Constantes reales 66
continue 149
Control del cursor 378
Conversi6n de tipos 82
Conversi6n explicita del tipo de una expresi6n 85
Convertir coordenadas fisicas a 16gicas y
viceversa 741
Coordenadas
fisicas 740
16gicas 740
reales en una ventana 775
cos 294
cosh 295
cprintf 377
cputs 377
Creaci6n de una enumeraci6n 58
Creaci6n de una mascara 753
Crear un fichero ejecutable 550
creat 360
cscanf 377
ctime 300
ctype.h 858
Cuerpo de la funci6n 255
CVPACK 616
Declaracion de constantes 72
Declaraci6n de funciones a nivel interno y a nivel
externo 108
Declaracion de funciones far 0 near 530
Declaracion de una funci6n 95, 257
Declaraci6n de variables near, far, huge 0 based 529
Declaraciones complejas 248
Declaraciones y definiciones 92
defined 397
Definici6n de una funci6n 96, 252
Depuracion 47; 596
Depurar un programa 45
Detecci6n de errores 318, 324
direct.h 858
Directorios y carninos 669
div 302
do 143
dos.h 859
dosexterr 691
double 60
dup 369
dup2369
Edici6n 45
Edici6n de un programa 42
Ejecuci6n de procesos 702
Ejecuci6n de un proceso 699, 702
ensamblador 619, 641
Entrada y salida estandar 110
Entrada/salida
caracter a caracter 326
con formato 336
de cadenas de caracteres 332
palabra a palabra 330
utilizando registros 0 bloques 338
enum 57
eof 361
errno.h 860
Espacios en blanco 52
Espedficaci6n de un path 673
Estructura de un programa C 89
Estructura de un programa gnl.fico 726
Estructura de un programa para presentaciones
graficas 785
Estructura para almacenar la configuracion de
video 731
Estructuras 197, 413
Creaci6n de una estructura 197
Operaciones con estructuras 200
execxxx 712
EXEHDR 617
exit 702
exp 295
EXP 618
Expresiones 94
condicionales 79
de Boole 76
numericas 73
de ficheros 553
Extensiones
fabs 2%
fclose 323
fcloseal1 323
fcntl.h 860
fcvt 191
fdopen 321
feof 325
ferror 324
fflush 124, 344
fgetc 328
fgetpos 352
fgets 333
ficheros de cabecera (.h) 91, 403
Ficheros temporales 345
fileno 362
float 59
float.h 860
floor 2%
fopen 319
for 145
for bucles anidados 146
FP_OFF 653
FP-1)EG 654
fprintf 336
fputc 326
fputs 332
fread 338
free 245
freopen 322
fscanf 336
fseek 348
fsetpos 352
ftell 349
Funciones 95
con un numero de argumentos variable 276
gnificas 742
intrinsecas 401, 407
matemliticas 292
para asignacion dimimica de memoria 240
para clasificacion y conversion de caracteres 193
para control de directorios 674
para control de procesos 701
para conversion de datos 190
para entrada/salida 363
para la consola 373
para llamar al DOS 650
para los puertos de E/S 384
para manipulacion de ficheros 679
para manipular cadenas de caracteres 183
para obtener 0 poner atributos 749
para presentaciones gnificas 787
para representar distintos tipos de letras 797
para un sistema de coordenadas cartesianas 779
predefinidas en C 292
prototipo 257
recursivas 279
referentes al uso de paletas 748
re1ativas a configuracion 742
relativas a coordenadas 745
fwrite 338
getch 125, 373
getchar 122
getche 125, 374
getcwd 676
getenv 720
gets 175
getw 330
goto y etiquetas 150
graph.h 860
halloc 246
hash abierto 499, 503
hash con overflow 501
hash. Eliminacion de elementos 502
HELP MAKE 617
hfree 246
Historia del lenguaje C 39
huge 523
Identificadores 68
if 129
if anidamiento de sentencias 131
if estructura 134
IUNK 568
Iniciacion de un proceso 698
Inicializacion de cadenas 232
inp 385
inpw 386
Instalacion 848
int 56
int86650
int86x 651
intdos 651
intdosx 652
Interconexion de entradas y salidas estandar 672
io.h 861
isalnum 193
isalpha 193
isascii 193
isatty 685
iscntrl 194
isdigit 194
isgraph 194
islower 194
isprint 194
ispunct 194
isspace 195
isupper 195
isxdigit 195
itoa 191
labs 296
large 520
Letras, digitos y caracter de subrayado 52
Leyendo y escribiendo datos 318
lfind 306
LIB 572
LIB con respuestas automaticas 576
LIB en modo pregunta/respuesta 575
limits.h 861
LINK 560
LINK con respuestas automaticas 566
LINK en modo pregunta/respuesta 565
Listas circulares 436
Listas doblemente enlazadas 444
Listas lineales 415
Operaciones basicas 418
Llamada a una funcion 97, 256
Llamando a un pr0cedimiento en ensamblador
desde C 644
localtime 301
log 295
log10 296
long 56
long double 61
longjmp 704
lsearch 306
lseek 366
ltoa 192
macros 392
macros 0 funciones 408
malloc 240
malloc.h 861
manipulacion de bloques de memoria 542
Manipulacion de ficheros 316, 335
math.h 862
matherr 297
medium 518
memccpy 542
memchr 543
memcmp 543
memcpy 544
memicmp 543
memmove 544
memoria intermedia asociada con un fichero 341
memory.h 862
memset 544
Menus de PWB 806
Metodo de insercion 477
Metodo de la burbuja 473
Metodo Quicksort 480
mkdir 675
Modalidades de video disponibles 728
Modelos de meploria estandar 515
Modelos de memoria mixtos 526
NMAKE 579
Caracteres que pueoen modificar macros 585
Componentes de una descripcion de fichero 591
con respuestas automaticas 592
Directrices 588
Ficheros en linea 590
Inicializacion automatica. TOOLS.INI 593
Macros 583
Macros especiales 584
makefile 579
Opciones de NMAKE 582
Prioridades 589
Pseudoobjetivos 592
Reglas de inferencia 586
Simbolos especiales 591
Sustituciones en macros 584
Nombres de ficheros y extensiones 49
Nombres de tipos 63
Numeros pseudoaleatorios 158
Ca1culo de areas y volumenes 161
onexit 703
Opciones de CL 554
open 357
Operaciones con directorios 673
Operador
# 394
# # 394
coma 79
de direccion-de (&) 80
de indireccion (.) 80
sizeof (tamafto de) 80
Operadores 73
aritmeticos 74
de asignacion 77
de relacion 75
logicos 74
logicos para manejo de bits 76
unitarios 76
Ordenacion de ficheros en disco 488
Acceso aleatorio 494
Acceso secuencial 488
outp 385
outpw 386
Overlays 567
Palabras clave 69
Parametros de inicializacion del puerto 664
Parametros por valor 0 por referencia 267
Pasando argumentos a funciones 98, 268
perror 326
Pilas 427
pow 297
Preparando un programa simple 45
preprocesador 391, 405
Presentacion de la sintaxis de C 51
printf 110
Prioridad y orden de evaluacion 81
process.h 862
Programa C formado por multiples ficheros 99
Programas residentes 692
Prompt 672
Punteros 22, 219
a cadenas de caracteres 227, 236
a estructuras 247
a funciones 288
a objetos de tipo no especificado (void) 225
a punteros 233
basad os en su propio segmento 539
basad os en un segmento 514
basados en un segmento constante 532
basados en un segmento variable 533
basados en void 538
basados sobre un puntero 536
Comparacion de punteros 223
Creacion de punteros 219
far 511
huge 514
near 511
nulos 524
Operacion de asignacion 222
Operaciones aritmeticas 222
Operaciones con punteros 222
Operadores 220
y arrays 226
y segmentos de 64K 510
putch 374
putchar 122
putenv 720
puts 175
putw 330
PWB 805
Browse 842
Caracteristicas del editor del PWB 814
con raton (mouse) 809
Copiar texto de otros ficheros 829
Edit 818
File 811
menu principal 808
Help 844
Make 831
Moviendo y copiando texto 821
Operaciones con el editor 816
Options 836
PWB
Run 835
Search 824
Seleccionando texto 815
Ventanas de dialogo 810
View 822
QH 617
qsort 303
raise 710
rand 299
read 363
Realizacion de un programa en C 42
realloc 244
Recursividad 465
Redireccion de la entrada 671
Redireccion de la salida 671
remove 683
rename 683
Restaurar la modalidad de video original 730
return 97, 255
rewind 349
RM 618
rmdir 675
rmtmp 345
Rutinas en lenguaje ensamblador en linea con
sentencias C 620
Salvar el programa 44
scanf 116
search.h 863
Secuencias de escape 53
segread 653
Seleccionar la modalidad de video 729
Sentencia compuesta 0 bloque 94
Sentencia de asignacion 109
Sentencias 94
Servicios del BIOS 658
setbuf 341
setjmp 704
setjmp.h 863
setmode 683
SETUP 849
setvbuf 341
share.h 863
short 55
signal 707
signaI.h 863
sin 294
sinh 295
Sintaxis de las sentencias y funciones de C 108
small 517
sopen 369
Soporte MS-DOS
para llamadas al sistema 687
para asignaci6n de memoria 540
para cadenas de caracteres 542
para manipulaci6n de bloques de memoria 546
spawnxxx 716
sprintf 730
sqrt 297
srand 299
stack 281
stat 684
stdarg.h 863
stddef.h 864
stdio.h 864
stdlib.h 865
strcat 183
strchr 184
strcmp 184
strcpy 184
strcspn 184
strdup 188
strerror 189
string.h 866
strlen 185
strlwr 189
strncat 186
strncmp 186
strncpy 186
strnset 189
strrchr 186
strset 189
strspn 187
strstr 187
strtok 187
strupr 189
switch 135
sys  locking.h 866
sys  stat.h 866
sys  timeb.h 866
sys  types.h 867
sys  utime.h 867
system 126, 677
Tamafio de una variable tipo puntero 510
tan 295
tanh 295
tell 367
tempnam 347
Terminaci6n de procesos 700, 701
time 300
time.h 867
tiny 516
Tipos de datos 53
Tipos de letras (fonts) 796
Tipos derivados 61
Tipos estandar 86
Tipos fundamentales 54
tmpfile 345
tmpnam 346
toascii 195
tolower 195
toupper 196
typedef 63
umask 370
UNDEL 618
ungetch 375
Uniones 201
unlink 682
Utilizaci6n de dispositivos estandar 334
Utilizaci6n de punteros basad os en un
segmento 530
utime 685
varargs.h 867
Variables 70
dedaradas a nivel externo 104
dedaradas a nivel interne 106
globales y locales 101
Visualizar imagenes 754
Visualizar texto 761
void 61
volatile 73
while 140
write 363
Del mismo autor
• Curso de programaci6n con
PASCAL
ISBN: 84-86381-36-3
224 pags.
• Curso de programaci6n
GW BASIC/BASICA
ISBN: 84-86381-87-8
320 pags.
• Manual para
TURBO BASIC
Guia del Programador
• Manual para
Quick C 2
Guia del programador
• Manual para
Quick BASIC 4.5
Guia del programador
• Curso de programaci6n con
C
Microsoft C
ISBN: 84-86381-43-6
444 pags.
ISBN: 84-86381-65-7
540 pags.
ISBN: 84-86381-74-6
496 pags.
ISBN: 84-7897-052-5
512 pags.
• Curso de programaci6n
Microsoft COBOL
ISBN: 84-7897-001-0
480 pags.
• Curso de programaci6n
C++
Programaci6n Orientada a Objetos
ISBN: 84-7897-034-7
784 pags.
EnciclopediadellenguajeC.
C es uno de los lenguajes de programaci6n mas populares en la
actualidad. permite realizar una programaci6n estructurada sin lfmite a la
creatividad del programador V ademas los compiladores C tienen la ventaja
de produci:r programas reducidos y muv rapidos en ejecuci6n. Por ello,
muchos paquetes de &oftware estan escritos en C.
. Ellibro ha sido escrito p~ns~ndo par una parte en las personas que no
tenie'ndo cono~imientos de programaci6n C, desean aprenderlos; y por otra,
para los programadorts expertos, los cuales encontraran una exposici6n
completa dellenguaje C con todas las caracterfsticas que posee, V 10 que con
el se puede realizar. En resumen, incluve temas referentes a:
• Programaci6n C en base al disefio Top Down.
• Sentencias! funciones V punteros. :: .
• Estructuras, uniones, enumeraciones, cadenas V arrays.
• Ficheros. Acceso secuencial V aleatorio.
• EI preprocesador.
• Estrw:turas dinamicas.(listas V arboles).
• Algoritmos recursivos, de ordenaci6n V de busqueda.
• Manejo de Ia memoria.
• Compilador V eniazador ..
". Librerfas y utilidades.
• Rutina~ en Ienguaje ensamblador.
• Servicios del DOS y del BIOS.
• Graficos. .
• Entorno integ,rado de desarrollo.
Todo esto y mas se exp~me de forma clara y sencilla, con alrededor
de 175 PROBLEMAS RESUELTOS que Ie avudaran V serviran de base para
&USaplicaciones.
9 780201 625066
ISBN 0-201-62506-7
~J;.~ADDlsoN-wEsLE~ ..iBERoAMERlcANA
Billinghurst 897 PB-A, Buenos Aires 1174, Argentina
Ave. Brigadeiro Luis Antonio 2344, Conjunto 114,
Sao Paulo 01402, Sao PalJlo, Sr.lsil
Casilla 70060, Santiago 7, Chile
Apartado Aereo 241-943, Santa Fe de Bogota, Colombia
Espalter 3 baja, Madrid 28014, Espana
7 Jacob Way, Reading, Massachusetts 01867, E.U.A.
Apartado Postal 22-012, Mexico D.F. 14000, Mexico
Apartado Postal 29853, Rio Piedras, Puerto Rico 00929
Apartado Postal 51454, Caracas 1050·A, Venezuela

Más contenido relacionado

PDF
Criptografia
PDF
Web hacking-101-es
PDF
Refuerzo 6
PDF
100 experimentos-sencillos-de-fisica-y-quimica
PDF
Ingeniería aplicada de yacimientos petrolíferos craft & hawkins
PDF
Calculo diferencial
PDF
Curso de c++
Criptografia
Web hacking-101-es
Refuerzo 6
100 experimentos-sencillos-de-fisica-y-quimica
Ingeniería aplicada de yacimientos petrolíferos craft & hawkins
Calculo diferencial
Curso de c++

La actualidad más candente (13)

PDF
Índice del libro de 0xWord "Ataques en redes de datos IPv4 & IPv6" 3ª Edición
PDF
Cuaderno de ejercicios de cálculo diferencial
PDF
Balance de materiales en yacimientos de petoleo con gas disuelto
PDF
Engineering manual bge
PDF
Índice del libro de Windows Server 2016: Administración, Seguridad y Operaciones
PDF
38187212 algebra
PDF
Libro 100 experimentos sencillos fisica y quimica
PDF
TUTELA INHIBITORIA. Luiz Guilherme Marinoni. ISBN: 9788416212224
PDF
Tension en-la-red
PDF
Experimentos sencillos-de-fisica-y-quimica
PDF
EL PRECEDENTE EN EL DERECHO INGLÉS, Rupert Cross. ISBN: 9788497689397
PDF
Introduccion al lenguaje c
Índice del libro de 0xWord "Ataques en redes de datos IPv4 & IPv6" 3ª Edición
Cuaderno de ejercicios de cálculo diferencial
Balance de materiales en yacimientos de petoleo con gas disuelto
Engineering manual bge
Índice del libro de Windows Server 2016: Administración, Seguridad y Operaciones
38187212 algebra
Libro 100 experimentos sencillos fisica y quimica
TUTELA INHIBITORIA. Luiz Guilherme Marinoni. ISBN: 9788416212224
Tension en-la-red
Experimentos sencillos-de-fisica-y-quimica
EL PRECEDENTE EN EL DERECHO INGLÉS, Rupert Cross. ISBN: 9788497689397
Introduccion al lenguaje c
Publicidad

Similar a Lenguaje c (20)

PDF
MANUAL DE LENGUAJE C
PDF
Introducción a la programación en c
PDF
Introducción a la programación en C
PDF
Libro programación-en-c++
PDF
Introduccion a la_programacion_con_c
PDF
ApuntesC++.pdf
PDF
PDF
PDF
Practicas estructuras de datos y algoritmos
PDF
Manual de programacion lenguaje en C
PDF
Manual referencia cxx
PDF
El lenguaje de programación c++
PDF
Fundamentos de Programación con Lenguaje de Programación C++
PDF
Programacion en Phyton desde ce..........................ro
PDF
pensar_en_cpp-vol1.pdf
PDF
Programación en c j. carlos lopez ardao
PDF
Manual completo python
PDF
Python desde 0
PDF
Pensar en cpp
MANUAL DE LENGUAJE C
Introducción a la programación en c
Introducción a la programación en C
Libro programación-en-c++
Introduccion a la_programacion_con_c
ApuntesC++.pdf
Practicas estructuras de datos y algoritmos
Manual de programacion lenguaje en C
Manual referencia cxx
El lenguaje de programación c++
Fundamentos de Programación con Lenguaje de Programación C++
Programacion en Phyton desde ce..........................ro
pensar_en_cpp-vol1.pdf
Programación en c j. carlos lopez ardao
Manual completo python
Python desde 0
Pensar en cpp
Publicidad

Último (20)

PPTX
MANEJO DE QUIMICOS Y SGA GRUPO Mnsr Aleman.pptx
PPTX
CNE-Tx-ZyD_Comite_2020-12-02-Consolidado-Version-Final.pptx
PPT
357161027-seguridad-industrial-diapositivas-ppt.ppt
PPTX
Electronica II, material basico de electronica II
PDF
Pensamiento Politico Siglo XXI Peru y Mundo.pdf
PPTX
LEVANTAMIENTOS TOPOGRAFICOS - DIAPOSITIVAS
PDF
LIBRO UNIVERSITARIO DESARROLLO ORGANIZACIONAL BN.pdf
PPTX
ARQUITECTURA INTEGRAL EN OBRA, PRINCIPIOS BASICOS Y TERMINOS
PDF
LIBRO UNIVERSITARIO INTELIGENCIA ALGORITMICA BN.pdf
PDF
HISTORIA DE LA GRÚAA LO LARGO DE LOS TIEMPOSpdf
PDF
S15 Protección de redes electricas 2025-1_removed.pdf
PPTX
376060032-Diapositivas-de-Ingenieria-ESTRUCTURAL.pptx
PPTX
Software para la educación instituciones superiores
PPTX
CAPACITACIÓN DE USO ADECUADO DE EPP.pptx
PPTX
A8B08CED-D3D9-415C-B4A3-2A6CA6409A48.1.1Presentación Dirección 2022 unidade...
PDF
SESION 10 SEGURIDAD EN TRABAJOS CON ELECTRICIDAD.pdf
PDF
Sugerencias Didacticas 2023_Diseño de Estructuras Metalicas_digital.pdf
PDF
Prevención de estrés laboral y Calidad de sueño - LA PROTECTORA.pdf
PPTX
Riesgo eléctrico 5 REGLAS DE ORO PARA TRABAJOS CON TENSION
PDF
UD3 -Producción, distribución del aire MA.pdf
MANEJO DE QUIMICOS Y SGA GRUPO Mnsr Aleman.pptx
CNE-Tx-ZyD_Comite_2020-12-02-Consolidado-Version-Final.pptx
357161027-seguridad-industrial-diapositivas-ppt.ppt
Electronica II, material basico de electronica II
Pensamiento Politico Siglo XXI Peru y Mundo.pdf
LEVANTAMIENTOS TOPOGRAFICOS - DIAPOSITIVAS
LIBRO UNIVERSITARIO DESARROLLO ORGANIZACIONAL BN.pdf
ARQUITECTURA INTEGRAL EN OBRA, PRINCIPIOS BASICOS Y TERMINOS
LIBRO UNIVERSITARIO INTELIGENCIA ALGORITMICA BN.pdf
HISTORIA DE LA GRÚAA LO LARGO DE LOS TIEMPOSpdf
S15 Protección de redes electricas 2025-1_removed.pdf
376060032-Diapositivas-de-Ingenieria-ESTRUCTURAL.pptx
Software para la educación instituciones superiores
CAPACITACIÓN DE USO ADECUADO DE EPP.pptx
A8B08CED-D3D9-415C-B4A3-2A6CA6409A48.1.1Presentación Dirección 2022 unidade...
SESION 10 SEGURIDAD EN TRABAJOS CON ELECTRICIDAD.pdf
Sugerencias Didacticas 2023_Diseño de Estructuras Metalicas_digital.pdf
Prevención de estrés laboral y Calidad de sueño - LA PROTECTORA.pdf
Riesgo eléctrico 5 REGLAS DE ORO PARA TRABAJOS CON TENSION
UD3 -Producción, distribución del aire MA.pdf

Lenguaje c

  • 1. Enciclopedia del Lenguaje ""."" ADDISON-WESLEY IBEROAMERICANA
  • 3. Enciclopedia del Lenguaje cFeo. Javier Ceballos Sierra Profesor titular de la Escuela Universitaria Politecnica Universidad de Alcala de Henares (Madrid) TAT ADDISON-WESLEY IBEROAMERICANA
  • 4. Se ha puesto el maximo empefio en ofrecer allector una informacion completa y precisa. Sin embargo RA-MAEditorial y Addison-Wesley Iberoamericana, S.A.no asumen ninguna responsabilidad derivada de su usa, ni tam poco por cualquier violacion de patentes ni otros derechos de terceras partes que pudieran ocurrir. © 1993 por Addison Wesley Iberoamericana, S.A. Wilmington, Delaware, E.U.A. Ninguna parte de este libro puede ser reproducida, grabada en sistema de almacenamiento 0 transmitida en forma alguna ni por cualquier procedimiento, ya sea electronico, mecanico, reprografico, magnetico 0 cualquier otro, sin autorizacion previa y por escrito de RA-MA.
  • 5. Dedico esta obra a Marfa del Carmen, mi esposa, y a mis hijos Francisco y' Javier
  • 6. CAPITULO 1. Introduccion al Lenguaje C. . . . . . . . . . . . . . . 39 Historia del lenguaje C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Realizaci6n de un programa en C. . . . . . . . . . . . . . . . . . . . . 41 Edici6n de un programa. . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Salvar el programa escrito, en el disco. . . . . . . . . . . . . . . 44 Compilar y ejecutar el programa. . . . . . . . . . . . . . . . . . . . 44 Salvar el programa ejecutable (.exe), en el disco. . . . . . . 44 Depurar un programa............................. 45 Preparando un programa simple. . . . . . . . . . . . . . . . . . . . 45 Edici6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Compilaci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Depuraci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Nombres de ficheros y extensiones. . . . . . . . . . . . . . . . . . . . . 49 CAPITULO 2. Elementos del Lenguaje C........ .... .... 51 Presentaci6n de la sintaxis de C. . . . . . . . . . . . . . . . . . . . . . . 51 Caracteres de C. .. .. . . . . . . . . . . . . . .. . .. . .. .. .. . . . . . . . 52
  • 7. Letras, digitos y carcicter de subrayado. . . . . . . . . . . . . . . 52 Espacios en blanco. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Caracteres especiales y signos de puntuaci6n. . . . . . . . . 53 Secuencias de escape.............................. 53 Tipos de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Tipos fundamentales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 char. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 short. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 long. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 enum. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Creaci6n de una enumeraci6n. . . . . . . . . . . . . . . . . . . . . . 58 float. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 double. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 long double...................................... 61 void. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Tipos derivados..................................... 61 punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 uni6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Nombres de tipos................................... 63 typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Constantes enteras................................ 65 Constantes reales................................. 66 .. ". Constante de un solo carcicter. . . . . . . . . . . . . . . . . . . . . . 67 .Constantes de caracteres........................... 67 'Identificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Palabras clave. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Declaraci6n de constantes............................ 72 Cailficador const .. " . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Calificador volatile.... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Expresiones numeric'as............................... 73 Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Operadores aritmeticos............................ 74 Operadores 16gicos................................ 74 Operadores de relaci6n............................ 75
  • 8. Expresiones de Boole. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Operadores unitarios.............................. 76 Operadores 16gicos para manejo de bits. . . . . . . . . . . . . . 76 Operadores de asignaci6n.......................... 77 Expresiones condicionales.......................... 79 Otros operadores.................................... 79 Operador coma................................... 79 Operador de indirecci6n (*)........................ 80 Operador de direcci6n-de (&). . . . . . . . . . . . . . . . . . . . . . . 80 Operador sizeof (tamafio de).. . . . . . . . . . . . . . . . . . . . . . 80 Priori dad y orden de evaluaci6n. . . . . . . . . . . . . . . . . . . . . . . 81 Conversi6n de tipos................................. 82 Conversi6n explicita del tipo de una expresi6n. . . . . . . . . . 85 Tipos estandar...................................... 86 CAPITUW 3. Comenzando con el Lenguaje C. . . . . . . . . . . 89 Estructura de un programa C. . . . . . . . . . . . . . . . . . . . . . . . . 89 Ficheros de cabecera. Directriz # include. . . . . . . . . . . . . 91 Directriz # define. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Declaraciones y definiciones. . . . . . . . . . . . . . . . . . . . . . . . 92 Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Sentencias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Sentencia compuesta 0 bloque. . . . . . . . . . . . . . . . . . . . . . 94 Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 Declaraci6n de una funci6n. . . . . . . . . . . . . . . . . . . . . . . . 95 Definici6n de una funci6n......................... 96 Llamada a una funci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Pasando argumentos a funciones.................... 98 Un programa C formado por multiples ficheros. . . . . . . . . 99 Accesibilidad de variables. Ambito.................... 101 Variables glob ales y locales. . . . . . . . . . . . . . . . . . . . . . . . . 101 Clases de almacenamiento. . . . . . . . . . . . . . . . . . . . . . . . . . 103 Variables declaradas a nivel externo. . . . . . . . . . . . . . . . . 104 Variables declaradas a nivel interno. . . . . . . . . . . . . . . . . . 106 Declaraci6n de funciones a nivel interno y a nivel externo 108 Sintaxis de las sentencias y funciones de C. . . . . . . . . . . . . 108 Senten cia de asignaci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 Entrada y salida estandar............................ 110 Salida con formato. Funci6n printf. . . . . . . . . . . . . . . . . . . . 110 Entrada con formato. Funci6n scanf. . . . . . . . . . . . . . . . . . . 116 Entrada de caracteres. getchar........................ 122
  • 9. Salida de caracteres. putchar. . . . . . . . . . ... . . . . . . . . . . . . . . 122 Caracter fin de linea y caracter fin de fichero. . . . . . . . . . . 123 Funciones getch y getche. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 Funci6n system..................................... 126 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 CAPITUW 4. Sentencias de Control.................... 129 Senten cia if. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 Anidamiento de sentencias if.. . . . . . . . . . . . . . . . . . . . . . . . 131 Estructura if. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 Sentencia switch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Sentencia break..................................... 138 Sentencia while..................................... 140 Sentencia do. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 Senten cia for....................................... 145 Bucles anidados..................................... 146 Sentencia continue.................................. 149 Sentencia goto y etiquetas. . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 Numeros pseudoaleatorios............................ 158 Calculo de areas y volumenes. . . . . . . . . . . . . . . . . . . . . . 161 CAPITUW 5. Tipos Estructuradosde Datos............. 163 Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Declaraci6n de un array. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 Arrays unidimensionales........................... 164 Arrays multidimensionales......................... 165 Caracteristicas generales........................... 166 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Cadenas de caracteres. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Funci6n gets. Leer una cadena de caracteres. . . . . . . . . . . . 175 Funci6n puts. Escribir una cadena de caracteres. . . . . . . . . 175 Limpiar el buffer asociado con stdin. . . . . . . . . . . . . . . . . . . 176 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 Arrays de cadenas de caracteres. . . . . . . . . . . . . . . . . . . . . . . 182 Funciones para manipular cadenas de caracteres. . . . . . . . . 183 Funciones para conversi6n de datos. . . . . . . . . . . . . . . . . . . . 190 Funciones para clasificaci6n y conversi6n de caracteres. . . 193 Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 Creaci6n de una estructura. . . . . . . . . . . . . . . . . . . . . . . . . 197 Operaciones con estructuras. . . . . . . . . . . . . . . . . . . . . . . . 200
  • 10. Arrays de estructuras.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 Union~s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 Campos de bits .. ; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 CAPITUW 6. Punteros.... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Creaci6n de punteros................................ 219 Operadores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 Importancia del tipo del objeto al que se apunta. . . . . . 221 Operaciones con punteros............................ 222 Operaci6n de asignaci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . 222 Operaciones aritmeticas............................ 222 Comparaci6n de punteros. . . . . . . . . . . . . . . . . . . . . . . . . . 223 Ejemplos con punteros............................ 223 Punteros a objetos de tipo no especificado (void). . . . . 225 Punteros y arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 Punteros a cadenas de caracteres.. . . . . 227 Inicializaci6n de cadenas. . . . . . . . . . . . . . . . . . . . . . . . . . . 232 Arrays de punteros. Punteros a punteros. . . . . . . . . . . . . . . 233 Inicializaci6n de un array de punteros a cadenas de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 Asignaci6n dimimica de memoria. .. ... . . . . . ... . . .. . . . 239 Funciones para asignaci6n dinamica de memoria. . . . . . . . 240 malloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 Arrays dinamicos................................. 242 calloc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 realloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 free. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 halloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 hfree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 Punteros a estructuras............................... 247 Declaraciones complejas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 CAPITUW 7. Funciones............................... 251 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 Definici6n de una funci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 Cuerpo de la funci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 Valor retornado por una funci6n. Sentencia return. . . . 255 Llamada a una fund6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 Declaraci6n de una' funci6n. (Funci6n prototipo). . . . . . . . 257
  • 11. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 Pasando parametros por valor 0 por referencia. . . . . . . . . . 267 Pasando arrays completos. . . . . . . . . . . . . . . . . . . . . . . . . . 268 Pasando punteros................................. 272 Argumentos en la linea de 6rdenes. . . . . . . . . . . . . . . . . . . . 274 Funciones con un numero de argumentos variable. . . . . . . 276 Funciones recursivas................................. 279 Ajustando e1tamafio del stack. . . . . . . . . . . . . . . . . . . . . . 281 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 Punteros a funciones................................ 288 Funciones predefinidas en C. . . . . . . . . . . . . . . . . . . . . . . . . . 292 Funciones matematicas............................ 292 Otras funciones de interes. . . . . . . . . . . . . . . . . . . . . . . . . . 299 Funci6n C para clasificar datos. . . . . . . . . . . . . . . . . . . . . . . 303 qsort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303 Funciones C para busqueda.......................... 305 bsearch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308 CAPITULO 8. Funciones ESbindar de E/S................ 315 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 Manipulaci6n de ficheros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 Abriendo un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317 Cerrando un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318 Leyendo y escribiendo datos. . . . . . . . . . . . . . . . . . . . . . . . 318 Detecci6n de errores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318 Acceso secuencial y acceso aleatorio. . . . . . . . . . . . . . . . . 319 Abrir un fichero.................................... 319 fopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319 fdopen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 freopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 Cerrar un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 fclose. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 fcloseall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 Detecci6n de errores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 ferror. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 clearerr. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 feof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 perror . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
  • 12. Entrada/salida canicter a caracter. . . . . . . . . . . . . . . . . . . . . 326 fputc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326 fgetc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328 Entrada/salida palabra a palabra. . . . . . . . . . . . . . . . . . . . . . 330 putw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 getw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 Entrada/salida de cadenas de caracteres. . . . . . . . . . . . . . . . 332 fputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332 fgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 Utilizaci6n de dispositivos estandar. . . . . . . . . . . . . . . . . . . . 334 Entrada/salida con formato.......................... 336 fprintf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 fscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 Entrada/salida utilizando registros 0 bloques. . . . . . . . . . . 338 fwrite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 fread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338 Control de la memoria intermedia asociada con un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 setbuf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 setvbuf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 fflush . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344 Ficheros temporales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 tmpfile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 rmtmp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 tmpnam . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 tempnam .. . . . . . . . . . . . . . . . 347 Acceso aleatorio a un fichero. . . . . . . . . . . . . . . . . . . . . . . . . 348 fseek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 ftell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 rewind. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 fsetpos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352 fgetpos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352 _fsopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 CAPITUW 9. FUDcioDes de E/S de bajo Dive'. . . . . . . . . . . . 355 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355 Manipulaci6n de ficheros , . 355 Abriendo un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 Cerrando un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 Leyendo y escribiendo datos. . . . . . . . . . . . . . . . . . . . . . . . 357 Abrir un fichero.................................... 357
  • 13. open. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 creat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 eof. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 fileno. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362 Cerrar un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 close. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 Funciones para entrada/salida........................ 363 write. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 read. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 Acceso aleatorio.................................... 366 lseek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366 tell. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367 dup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 dup2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 sopen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 umask. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370 CAPITUW 10. Funciones para la Consola y los Puertos de E/S 373 Introducci6n ". . . . . . . . . . . . . . . . . . . . 373 Funciones para la consola. . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 getch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 getche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374 putch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374 kbhit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 ungetch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 cgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 cputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 cscanf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 cprintf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 Control del cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378 Funciones para los puertos de E/S. . . . . . . . . . . . . . . . . . . . 384 inp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 outp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 inpw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386 outpw , . . . . . . . . . . . . . . . . . . 386 CAPITUW 11. EI Preprocesador de C. . . . . . . . . . . . . . . . . . . 391 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 Directriz # define. Sustituci6n de simbolos. . . . . . . . . . . . . . 392
  • 14. El operador #.................................... 394 El operador # # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 Directriz # undef. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 Directriz # include. Inclusi6n de ficheros fuente. . . . . . . . . 395 Compilaci6n condicional............................. 396 defined(identificador) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397 Directrices # ifdef e # ifndef. . . . . . . . . . . . . . . . . . . . . . . . . . 399 Directriz # line. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399 Directriz # error. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400 Directriz # pragma. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400 Funciones intrinsecas................................ 401 Utilizando ficheros de cabecera (.h). . . . . . . . . . . . . . . . . . . . 403 Utilizando el preprocesador. . . . . . . . . . . . . . . . . . . . . . . . . . . 405 Utilizando funciones intrinsecas. . . . . . . . . . . . . . . . . . . . . 407 Utilizando macros 0 funciones. . . . . . . . . . . . . . . . . . . . . . 408 CAPITUW 12. Estructuras Dimimicasde Datos.......... 413 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413 Asignaci6n dimimica de memoria..................... 414 Listas lineales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415 Operaciones basicas................................. 418 Inserci6n de un elemento al comienzo de la lista. . . . . . 418 Inserci6n de un elemento en general. . . . . . . . . . . . . . . . . 419 Borrar un elemento de la lista. . . . . . . . . . . . . . . . . . . . . . 420 Recorrido de una lista cuyo primer elemento esta apunta- do por p. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421 Buscar en una lista un elemento con un valor x. . . . . . . 422 Pilas, colas y listas doblemente enlazadas. . . . . . . . . . . . . . . 427 Pilas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427 Colas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432 Listas circulares. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436 Listas doblemente enlazadas. . . . . . . . . . . . . . . . . . . . . . . . 444 Arboles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 Arboles binarios.................................. 450 Recorrido de arboles'binarios............... . . . . .... 451 Arboles binarios de busqueda. . . . . . . . . . . . . . . . . . . . . . . . . 453 Borrado en arboles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458 Arboles binarios perfectamente equilibrados. . . . . . . . . . . . 460
  • 15. CAPITUW 13. Algoritmos Recursivos, de Ordenacion y de Btisqueda. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 Recursividad. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 Clasificaci6n de datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473 Metodo de la burbuja...... . . . . . . . . . . . . . . . . . . . . . . . 473 Metodo de inserci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477 Metodo Quicksort................................ 480 Comparaci6n de los metodos expuestos. . . . . . . . . . . . . . 484 Bdsqueda de datos. 485 Bdsque4a secuencial. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485 Bdsqueda binaria................................. 486 Ordenaci6n de ficheros en disco. . . . . . . . . . . . . . . . . . . . . . . 488 Ordenaci6n de ficheros. Acceso secuencial........... 488 Ordenaci6n de ficheros. Acceso aleatorio............ 494 Algoritmos hash.................................... 497 Arrays hash...................................... 498 Metodo hash abierto.............................. 499 Metodo hash con overflow. . . . . . . . . . . . . . . . . . . . . . . . . 501 Eliminaci6n de elementos.......................... 502 Un ejemplo de un array hash. . . . . . . . . . . . . . . . . . . . . . . 503 CAPITUW 14. Manejo de la Memoria. . . . . . . . . . . . . . . . . . 509 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509 Tamano de una variable tipo puntero. . . . . . . . . . . . . . . . . . 510 Punteros y segmentos de 64K. . . . . . . . . . . . . . . . . . . . . . . . . 510 Punteros near. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511 Punteros far........................................ 511 Punteros huge...................................... 514 Punteros basados en un segmento. . . . . . . . . . . . . . . . . . . . . 514 Modelos de memoria estandar. . . . . . . . . . . . . . . . . . . . . . . . 515 Modelo pequenito (tiny)........................... 516 Modelo pequeno (small)........................... 517 Modelo medio (medium). . . . . . . . . . . . . . . . . . . . . . . . . . . 518 Modelo compacta (compact)....................... 519 Modelo grande (large)............................. 520 Modelo enorme (huge)............................ 523 Punteros nul os...................................... 524 Modelos de memoria mixtos. . . . . . . . . . . . . . . . . . . . . . . . . . 526
  • 16. Declaraci6n de variables near, far, huge, 0 based. . . . . . . . 529 Declaraci6n de funciones far 0 near. . . . . . . . . . . . . . . . . . . 530 Utilizaci6n de punteros basad os en un segmento. . . . . . . . 530 Variables y punteros basados en un segmento constante 532 Punteros basados en un segmento variable. . . . . . . . . . . 533 Punteros basados sobre un puntero. . . . . . . . . . . . . . . . . . 536 Punteros basados en void. . . . . . . . . . . . . . . . . . . . . . . . . . 538 Punteros basados en su propio segmento. . . . . . . . . . . . . 539 Soporte MS-DOS para asignaci6n de memoria. . . . . . . . . . 540 Soporte MS-DOS para cadenas de caracteres. . . . . . . . . . . . 542 Manipulaci6n de bloques de memoria. . . . . . . . . . . . . . . . . . 542 Soporte MS-DOS para manipulaci6n de bloques de memoria. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546 CAPITULO 15. Compilar y Enlazar desde DOS. . . . . . . . . . . 549 Introducci6n................................. 549 Proceso para crear un fichero ejecutable. . . . . . . . . . . . . 550 Orden CL.......................................... 550 Extensiones de ficheros............................ 553 Opciones de CL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554 Generaci6n de c6digo............................. 554 Operaciones en coma flotante. . . . . . . . . . . . . . . . . . . . . . 555 Lenguaje. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556 Enlace. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556 Modelos de memoria. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557 Optimizaci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557 Ficheros de salida. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558 Preprocesador. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559 Listado de ficheros fuente. . . . . . . . . . . . . . . . . . . . . . . . . . 559 Opciones varias. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559 Orden LINK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 560 LINK en modo pregunta/respuesta. . . . . . . . . . . . . . . . . . 565 LINK con respuestas automatic as . . . . . . . . . . . . . . . . . . . 566 Overlays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567 Orden ILINK....................................... 568 CAPITULO 16. Librerias y Utilidades del Compilador. . . . . 571 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571 Orden LIB......................................... 572 LIB en modo pregunta/respuesta................... 575 LIB con respuestas automaticas.. . .. . . . .. .. 576
  • 17. Un ejemplo de trabajo con librerias. . . . . . . . . . . . . . . . . 577 Orden NMAKE..................................... 579 EI fichero makefile. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579 Opciones de NMAKE............................. 582 Macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583 Sustituciones en macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . 584 Macros especiales................................. 584 Caracteres que pueden modificar estas macros. . . . . . . . 585 Reglas de inferencia............................... 586 Directrices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588 Prioridades. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589 Ficheros en linea. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590 Simbolos especiales............................... 591 Componentes de una descripci6n de fichero. . . . . . . . . . 591 Pseudoobjetivos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592 NMAKE con respuestas automaticas................ 592 Inicializaci6n automatica. TOOLS.INI............... 593 Un ejemplo de trabajo con la utili dad NMAKE. . . . . . . 593 EI depurador Code View de Microsoft. . . . . . . . . . . . . . . . . 596 Com pilar y enlazar un programa C para depurar. . . . . 597 Invocando a Code View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597 Opciones de Code View........................... 598 Menus de Code View. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601 Code View con rat6n (mouse). . . . . . . . . . . . . . . . . . . . . . . . 603 Seleccionando texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604 Menu File.......................................... 604 Menu Edit......................................... 605 Menu View......................................... 606 Menu Search....................................... 609 Menu Run. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611 Menu Watch........................................ 612 Menu Options...................................... 614 Menu Calls......................................... 616 Otras utilidades suministradas con Microsoft C. . . . . . . . . 616 CVPACK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616 HELPMAKE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 BIND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 QH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 EXEHDR. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617 EXP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618
  • 18. RM .. . . . . . . . .. . . . . . . . . . . . . . . . 618 UNDEL......................................... 618 CAPITUW 17. Rutinas en Lenguaje Ensamblador. . . . . . . . 619 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619 Rutinas en lenguaje ensamblador en linea con sentencias C 620 El lenguaje ensamblador en bloques _asm. . . . . . . . . . . . . 622 COnstantes enteras................................ 623 Definici6n de datos............................... 623 Operadores y expresiones.......................... 623 Macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625 Comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625 Pseudoinstrucci6n _emit.......................... 625 Utilizando elementos de C en un bloque _asm. . . . . . . . . 626 Definici6n de macros en lenguaje ensamblador. . . . . . . . . . 629 Utilizando y salvando los registros. . . . . . . . . . . . . . . . . . . . . 630 Formas de utilizar un bloque _asm. . . . . . . . . . . . . . . . . . . 633 Llamando a funciones C. . . . . . . . . . . . . . . . . . . . . . . . . . . 633 Reemplazar una funci6n C. . . . . . . . . . . . . . . . . . . . . . . . . 634 Manipulaci6n de interrupciones. . . . . . . . . . . . . . . . . . . . . 635 Trabajando con punteros. . . . . . . . . . . . . . . . . . . . . . . . . . . 635 Trabajando con estructuras. . . . . . . . . . . . . . . . . . . . . . . . . 637 Salto a una etiqueta. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640 Depuraci6n y optimizaci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . 641 M6dulos separados en lenguaje ensamblador. . . . . . . . . . . . 641 Entrando al procedimiento. . . . . . . . . . . . . . . . . . . . . . . . . 642 Salvar el valor de los registros. . . . . . . . . . . . . . . . . . . . . . 642 Acceso a los parametros de la pila. . . . . . . . . . . . . . . . . . 643 Devolver un valor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644 Llamando a un procedimiento en ensamblador des de C. . 644 CAPITUW 18. Comunicaciones. Servicios del DOS y del BIOS 649 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649 Funciones para Hamar al DOS. . . . . . . . . . . . . . . . . . . . . . . . 650 int86 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650 int86x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651 intdos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651 intdosx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652 segread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653 bdos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653 FP _OFF. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
  • 19. FP_SEG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654 Un ejemplo de comunicaciones. . . . . . . . . . . . . . . . . . . . . 654 Un ejemplo de llamadas al DOS. . . . . . . . . . . . . . . . . . . . 656 Servicios del BIOS.................................. 658 Servicios del disco. _bios_disk. . . . . . . . . . . . . . . . . . . . 658 Listado del equipo. _bios_equiplist. . . . . . . . . . . . . . . . 660 Servicios del teclado. _bios_keybrd. . . . . . . . . . . . . . . . 660 Tamafio de la memoria. _bios-lIlemsize. . . . . . . . . . . . 661 Servicios del puerto paralelo. _bios_printer. . . . . . . . . 662 Servicios del puerto serie. _bios_serialcom. . . . . . . . . . 664 Panimetros de inicializaci6n del puerto. . . . . . . . . . . . . . 664 Servicios del reloj. _bios_timeofday............... 666 CAPITUW 19. C y DOS........ . . . . . . . . . . . . . . . . . . . . . . 669 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669 Directorios y caminos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669 Definiciones generales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669 Camino (path). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669 Redirecci6n de la salida. . . . . . . . . . . . . . . . . . . . . . . . . . . . 671 Redirecci6n de la entrada. . . . . . . . . . . . . . . . . . . . . . . . . . 671 Interconexi6n de entradas y salidas estandar. . . . . . . . . . 672 Prompt. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672 Operaciones con directorios. . . . . . . . . . . . . . . . . . . . . . . . . . . 673 Especificaci6n de un path. . . . . . . . . . . . . . . . . . . . . . . . . . . . 673 Funciones para control de directorios. . . . . . . . . . . . . . . . . . 674 chdir. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674 mkdir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675 rmdir. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675 getcwd. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676 system. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677 Funciones para manipulaci6n de ficheros. . . . . . . . . . . . . . . 679 access. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679 chmod. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680 chsize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 unlink. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 rename. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683 setmode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683 stat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684 isatty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685 utime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685 Utilizando c6digos de salida. . . . . . . . . . . . . . . . . . . . . . . . . . 686
  • 20. Soporte DOS para llamadas al sistema. . . . . . . . . . . . . . . . . 687 Programas residentes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692 CAPITUW 20. Control de Procesos. . . . . . . . . . . . . . . . . . . . . 697 Introducci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697 Iniciaci6n de un proceso..... . . . . . . . . . . . . . . . . . . . . . . . . 698 Ejecuci6n de un proceso.... . . . . . . . . . . . . . . . . . . . . . . . . . 699 Terminaci6n de un proceso. . . . . . . . . . . . . . . . . . . . . . . . . . . 700 Funciones para control de procesos. . . . . . . . . . . . . . . . . . . . 701 Terminaci6n de procesos............................. 701 abort. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . 701 exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702 Ejecuci6n de procesos............................... 702 atexit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702 onexit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703 setjmp. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704 longjmp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704 _fpreset. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 signal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707 raise. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710 Comenzar un nuevo proceso. . . . . . . . . . . . . . . . . . . . . . . . . . 712 execxxx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712 spawnxxx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716 getenv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720 putenv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720 CAPITUW 21. Gnificos con C. . . . . . . . . . . . . . . . . . . . . . . . . 725 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725 Estructura de un programa gnifico........... 726 Modalidades de video disponibles........... . . . . . . . . . . 728 Seleccionar la modalidad de video. . . . . . . . . . . . . . . . . . 729 Restaurar la modalidad de video original. . . . . . . . . . . . . 730 Almacenar caracteres. Funci6n sprintf. . . . . . . . . . . . . . . 730 Estructura para almacenar la configuraci6n de video. . 731 Colores en modo texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734 Colores en modo gnifico utilizando CGA. . . . . . . . . . . . . . 736 Colores en modo gnifico utilizando VGA, MCGA y EGA 739
  • 21. Especificaci6n de coordenadas. . . . . . . . . . . . . . . . . . . . . . . . 740 CO'ordenadas fisicas............................... 740 Coordenadas 16gicas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 740 Volviendo a coordenadas fisicas. . . . 741 Convertir coordenadas fisicas a 16gicas y viceversa. . . . 741 Funciones gnificas .... ; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742 Funciones relativas a configuraci6n. . . . . . . . . . . . . . . . . . . . 742 Funciones relativas a coordenadas. . . . . . . . . . . . . . . . . . . . . 745 Funciones referentes al uso de paletas. . . . . . . . . . . . . . . . . . 748 Funciones para obtener 0 poner atributos. . . . . . . . . . . . . . 749 Creaci6n de una mascara. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753 Visualizar imagenes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 754 Visualizar texto..................................... 761 Animaci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764 Animaci6n de un objeto............................. 766 Ejercicio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771 Coordenadas reales en una ventana. . . . . . . . . . . . . . . . . . . . 775 Funciones para un sistema de coordenadas cartesianas. . . 779 CAPITUW 22. Presentaciones Gnificas. . . . . . . . . . . . . . . . . . 785 Introducci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785 Estructura de un programa para presentaciones graficas. . 785 Funciones para presentaciones graficas. . . . . . . . . . . . . . . . . 787 Tipos de letras (fonts)............................... 796 Funciones para representar distintos tipos de letras. . . . . . 797 CAPITUW 23. Utilizaci6n del PWB.................... 805 PWB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805 Menus de PWB. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806 El menu principal....... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808 PWB con rat6n (mouse)............................. 809 Ventanas de dialogo................................. 810 El menu File....................................... 811 Caracteristicas del editor del PWB... . . . . . 814 Seleccionando texto............................... 815 Operaciones con el editor............................ 816 Mover el cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816 Scroll. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817 Insertar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817
  • 22. Borrar '. . . . . . . . . . . . . . . . 817 Seleccionar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 817 Copiar, mover 0 borrar el texto seleccionado. . . . . . . . . 817 Buscar y sustitu~..... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818 Teclas de funci6n................................. 818 Menu Edit......................................... 818 Moviendo y copiando texto. . . . . . . . . . . . . . . . . . . . . . . . . . . 821 Menu View......................................... 822 Menu Search....................................... 824 Copiar texto de otros ficheros. . . . . . . . . . . . . . . . . . . . . . . . . 829 Programas y m6dulos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830 Menu Make........................................ 831 Menu Run.. . . . . . . . .. . . . . . . . . 835 Menu Options...................................... 836 Menu Browse....................................... 842 Menu Help......................................... 844 CAPITUW 24. Instalaci6n de Microsoft C. . . . . . . . . . . . . . . 847 Sistema Requerido. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847 Caracteristicas aportadas a partir de la versi6n 6. . . . . . . . 848 Instalaci6n . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 848 Ejecuci6n del programa SETUP. . . . . . . . . . . . . . . . . . . . . . . 849 APENDICE A. Ficheros .h de C........................ 857 Ficheros de cabecera, variables globales y tipos 857 APENDICE B. C6digos de caracteres (ASCII). . . . . . . . . . . . 869 C6digos extendidos.................................. 875 C6digos del teclado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
  • 23. Saludo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Cuadrados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Paso de grados Centigrados a Fahrenheit (F=9/5*C+32). . . . . 90 Funci6n intercambio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Valor mayor de tres valores dados. . . . . . . . . . . . . . . . . . . . . . . . .. 100 Ambito de las variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 102 Declaraci6n de extern a nivel externo. . . . . . . . . . . . . . . . . . . . . .. 104 Declaraciones a nivel interno. . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 107 Leyendo datos de la entrada estandar. . . . . . . . . . . . . . . . . . . . . .. 123 Capital e Intereses....................................... 127 Soluci6n de una ecuaci6n de segundo grado. . . . . . . . . . . . . . . .. 128 Menor de tres numeros a, bye. . . . . . . . . . . . . . . . . . . . . . . . . . .. 133 Cantidad a pagar en funci6n de la cantidad comprada. . . . . . .. 134
  • 24. Dias correspondientes a un mes de un ano dado. . . . . . . . . . . .. 137 Importe por vehkulo al circular por una autopista. . . . . . . . . .. 138 Simulaci6n de una maquina sumadora. . . . . . . . . . . . . . . . . . . . .. 141 C6digo ASCII de un caracter. . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 141 Cuadrados que se pueden expresar como suma de otros dos .. 142 Raiz cuadrada de un numero. Metodo de Newton. . . . . . . . . . .. 143 Construir un triangulo de n filas con caracteres. . . . . . . . . . . . .. 147 Tablero de Ajedrez. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 148 Areas de circulos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 150 goto salir............................................... 151 Calcular las rakes de una ecuaci6n de 2? grado. . . . . . . . . . . .. 152 Palabras con cuatro 0 mas vocales diferentes. . . . . . . . . . . . . . .. 154 Contar caracteres, palabras y lineas en un texto. . . . . . . . . . . . .. 155 Simulaci6n de una calculadora............................ 156 Tirada de un dado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 159 Valores entre 0 y 1....................................... 160 Numeros pseudoaleatorios - Volumen de una esfera. . . . . . . . .. 161 Creaci6n de un array unidimensional. . . . . . . . . . . . . . . . . . . . . .. 168 Nota media del curso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 169 Crear un array bidimensional. . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 170 Tanto por ciento de aprobados. . . . . . . . . . . . . . . . . . . . . . . . . . . .. 171 Encontrar el maximo y e1 minimo de un conjunto de valores. 172 Limpiar el buffer asociado con stdin. . . . . . . . . . . . . . . . . . . . . .. 177 Examinar una cadena de caracteres almacenada en memoria.. 178 Linea de texto y calcular su longitud. . . . . . . . . . . . . . . . . . . . . .. 179 Conversi6n de mayusculas a minusculas. . . . . . . . . . . . . . . . . . .. 181 Leer una lista de nombres. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 182 Funciones para manipular cadenas de caracteres. . . . . . . . . . . .. 185 Funci6n strtok. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 188 Funciones para clasificaci6n y conversi6n de datos. . . . . . . . . .. 196 Calcular el 070 de aprobados y suspensos. . . . . . . . . . . . . . . . . . .. 200 Biblioteca compuesta por libros y revistas. . . . . . . . . . . . . . . . . .. 204 Campos de bits : . . . . . . . . . . . . . . . . . . . . . . .. 208 Tabla de frecuencias de letras adyacentes en un texto. . . . . . . .. 211 Cambio de atributos utilizando campos de bits. . . . . . . . . . . . .. 212 Manipulaci6n de un valor float bit a bit. . . . . . . . . . . . . . . . . . .. 216
  • 25. Visualizar el contenido de un bloque de memoria. . . . . . . . . . .. 224 Escribir los valores de un array. . . . . . . . . . . . . . . . . . . . . . . . . . .. 226 Funci6n "longstr" que devuelve la longitud de una cadena. .. 228 Funci6n para copiar una cadena en otra. . . . . . . . . . . . . . . . . . .. 230 Array de dos dimensiones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 234 Funci6n que devuelve el nombre del mes 1 a 12 dado. . . . . . . .. 236 Clasificar cadenas de caracteres. . . . . . . . . . . . . . . . . . . . . . . . . . .. 237 Asignaci6n de espacio para cadenas de caracteres. . . . . . . . . . .. 241 Asignaci6n de espacio para un array de enteros. . . . . . . . . . . . .. 243 Funciones real/oc y free. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 245 Punteros a estructuras. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 247 Busqueda Secuencial..................................... 260 Programa Alumnos...................................... 262 Leer una fecha, verificarla y escribirla con formato. . . . . . . . .. 265 Paso de parametros por referencia, utilizando punteros. . . . . .. 268 Pasando arrays a funciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 269 Linea de texto mas larga. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 270 Pasando punteros a funciones............................. 272 Argumentos en linea de 6rdenes. . . . . . . . . . . . . . . . . . . . . . . . . .. 275 Funciones con un numero de argumentos variable. . . . . . . . . . .. 278 Calculo del factorial de un numero. . . . . . . . . . . . . . . . . . . . . . . .. 280 Fusionar dos listas clasificadas. . . . . . . . . . . . . . . . . . . . . . . . . . . .. 282 Numero de veces que aparece cada letra en una cadena. . . . . .. 284 Calendario perpetuo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 286 Punteros a funciones..................................... 290 Funci6n matherr......................................... 298 Generar un numero aleatoric cada segundo. . . . . . . . . . . . . . . .. 301 Clasificaci6n de los elementos de una lista. . . . . . . . . . . . . . . . .. 304 Busqueda de un elemento en una lista. . . . . . . . . . . . . . . . . . . . .. 307 Clasificar lexicograficamente 0 numericamente. . . . . . . . . . . . . .. 309 Enscribir datos en un fichero caracter a caracter. . . . . . . . . . . .. 327 Leer datos de un fichero caracter a caracter. . . . . . . . . . . . . . . .. 328
  • 26. Contar los caracteres de un fichero. . . . . . . . . . . . . . . . . . . . . . . .. 329 Escribir y leer datos en un fichero palabra a palabra. . . . . . . .. 331 Entrada/salida de cadenas de caracteres. . . . . . . . . . . . . . . . . . . .. 333 Escribir el contenido de un fichero por la impresora. . . . . . . . .. 335 Escribir y leer datos con formato en un fichero. . . . . . . . . . . . .. 336 Escribir y leer datos en un fichero registro a registro 339 Control del buffer asociado a un fichero. . . . . . . . . . . . . . . . . . .. 342 Acceso aleatorio a un fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 350 Funci6n buscar.......................................... 353 Reproducci6n de la orden copy. . . . . . . . . . . . . . . . . . . . . . . . . . .. 364 Proceso de ficheros aleatorios con funciones de bajo nivel. . .. 367 Funciones para 1a consola. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 378 Presentaci6n de un menu......... . . . . . . . . . . . . . . . . . . . . . . .. 381 Emitir un sonido por el altavoz. . . . . . . . . . . . . . . . . . . . . . . . . . .. 387 Compilaci6n condicional. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 397 Utilizando ficheros de cabecera. . . . . . . . . . . . . . . . . . . . . . . . . . .. 403 Utilizando el preprocesador............................... 405 Medir tiempos de ejecuci6n. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 406 Funciones intrinsecas..................................... 407 Macros y Funciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 408 Crear objetos.............................. . . . . . . . . . . . . .. 417 Operaciones con listas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 423 Programa calculadora. Aplicaci6n de pilas. . . . . . . . . . . . . . . . .. 429 Realizaci6n de sucesos. Aplicaci6n de colas. . . . . . . . . . . . . . . .. 433 Listas circulares. Suma de ecuaciones algebraicas. . . . . . . . . . . .. 439
  • 27. Lista doblemente enlazada ordenada ascendentemente. . . . . . .. 444 Funciones recursivas para recorrer un arbol. . . . . . . . . . . . . . . .. 453 Arbol binario de busqueda. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 455 Borrar un nodo cualquiera de un arbol. . . . . . . . . . . . . . . . . . . .. 459 Arbol perfectamente equilibrado......................... .. 461 Funci6n de Ackerman recursiva. . . . . . . . . . . . . . . . . . . . . . . . . . .. 467 Funci6n de Ackerman no recursiva. . . . . . . . . . . . . . . . . . . . . . . .. 468 Torres de Hanoi......................................... 472 Clasificar lineas de texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 474 Ordenaci6n por inserci6n................................. 478 Ordenaci6n Quicksort.................................... 481 Busqueda Binaria........................................ 486 Ordenar un fichero. Acceso secuencial. . . . . . . . . . . . . . . . . . . . .. 490 Metodo de ordenaci6n Quicksort para ficheros... . . . . . . . . . .. 495 Metodo hash abierto............................ 503 Rellenar una ventana en la pantalla con el caracter ,car. . . . . .. 512 Punteros nulos.......................................... 524 Variables y punteros basados en un segmento constante. . . . . .. 532 Punteros basados en un segmento variable. . . . . . . . . . . . . . . . .. 533 Puntero basado en otro puntero. . . . . . . . . . . . . . . . . . . . . . . . . .. 536 Puntero basado en void. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 538 Puntero basado en su propio segmento................... .. 539 Manipulaci6n de areas de memoria , , . . .. 544 Un ejemplo de trabajo con librerias. . . . . . . . . . . . . . . . . . . . . . .. 577 Fichero de descripciones. NMAKE......................... 580 Un ejemplo de trabajo con la utilidad NMAKE. . . . . . . . . . . . .. 593
  • 28. Pagina activa.... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 620 Pulsar una tecla para continuar. . . . . . . . . . . . . . . . . . . . . . . . . . .. 621 Posicionar el cursor en la fila y columna indicadas. . . . . . . . . .. 627 Raiz cuadrada de un numero entero (_asm). . . . . . . . . . . . . . .. 631 Llamando a funciones C desde un bloque _asm. . . . . . . . . . .. 633 . Inicializar un array. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 634 Utilizaci6n de punteros en bloques _asm. . . . . . . . . . . . . . . . . .. 635 Trabajando con estructuras desde un bloque _asm. . . . . . . . .. 638 Utilizando etiquetas C y _asm. . . . . . . . . . . . . . . . . . . . . . . . . . .. 640 Raiz cuadrada de un numero entero. . . . . . . . . . . . . . . . . . . . . . .. 645 Leer datos del puerto serie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 654 Llamadas al DOS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 656 Servicios de impresora. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 662 Servicios del BIOS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 666 Visualizar los ficheros .EXE de un directorio dado. . . . . . . . . .. 676 Ejecutar 6rdenes del DOS con la funci6n system. . . . . . . . . . . .. 678 Cambiando los atributos de un fichero...... . 681 Programa residente (TSR). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 693 Funci6n atexit........................................... 703 Manipulaci6n de errores en coma flotante. . . . . . . . . . . . . . . . . .. 705 Manipulaci6n de senales de interrupci6n. . . . . . . . . . . . . . . . . . .. 710 Utilizaci6n de la funci6n exec. . . . . . . . . . . . . . . . . . . . . . . . . . . .. 714 Utilizaci6n de la funci6n spawn. . . . . . . . . . . . . . . . . . . . . . . . . . .. 718
  • 29. Estructura de un programa gnifico. . . . . . . . . . . . . . . . . . . . . . . .. 727 Modos de video disponibles en tu ordenador. . . . . . . . . . . . . . .. 731 Color de fondo y color del texto. . . . . . . . . . . . . . . . . . . . . . . . . .. 735 Color de fondo y de primer plano. . . . . . . . . . . . . . . . . . . . . . . .. 737 Sistema de coordenadas 16gicas.. . . . . . . . . . . . . . . . . . . . . . . . . .. 740 Alternar entre paginas de video. . . . . . . . . . . . . . . . . . . . . . . . . . .. 745 Portada para Microsoft C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 756 Colorear figuras......................................... 759 Ventanas de texto..................... . . . . . . . . . . . . . . . . . .. 763 Funciones para animaci6n de figuras. . ...•.. 767 Animaci6n - pe10ta rodando. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 769 Choque de una pelota contra una barrera. . . . . . . . . . . . . . . . . .. 770 Bola de billar........................................... (72 Representaci6n de la funci6n radio = cos(2 * alfa). . . . . . . . . .. 776 Representaci6n de la funci6n Y=2*cos(X)A2-sin(5+X). . . . . . .. 777 Representaci6n grafica utilizando tres ventanas. . . . . . . . . . . . .. 780 Presentaciones graficas - diagramas de barras y sectores. . . . .. 789 Presentaciones graficas multiples.............. 791 Presentaciones graficas por puntos......................... 794 Demostraci6n de tipos de letras. . . . . . . . . . . . . . . . . . . . . . . . . . .. 800
  • 30. Este es un libro que pretende cubrir dos objetivos: ser un manual para aprender C y ser una guia para el usuario de C. La forma en la que se ha estructurado ellibro ha sido precisamente, pensando en ese primer objetivo. El libro se ha dividido en veinticuatro capitulos que van presentando ellenguaje poco a poco, empezando por 10 mas sencillo, presentando cada tema a su tiempo, hasta llegar al final donde se habra visto todo 10 referente a la programacion en C y utilidades, sin apenas encontrar dificultades. El segundo objetivo queda conseguido al incluir en este libro todo 10 que un usuario quiere saber respecto a C, esto es, explicacion y desarrollo de todas las sentencias, estructuras, punteros, funciones, ficheros y direc- trices para compilador. Se completa el estudio de C con un capitulo refe- rente a estructuras dinamicas y otro de algoritmos de uso comun. Final- mente se estudian tecnicas avanzadas que abordan la materia referente a manejo de la memoria, compilacion yenlace, librerias, rutinas en lenguaje ensamblador, utilidades como el depurador de C, servicios del DOS y del BIOS, grcificos y una explicacion para el manejo del entorno de programa- cion del paquete de Microsoft C. Microsoft eversion 6, producto desarrollado por Microsoft, es un com- pilador C desarrollado para los ordenadores personales IBM y compati-
  • 31. 1. Introducci6n al lenguaje C 2. Elementos del lenguaje C 3. Comenzando con ellenguaje C 4. Sentencias de control 5. Tipos estructurados de datos 6. Punteros 7. Funciones 8. Funciones estandar de E/S 9. Funciones de E/S de bajo nivel 10. Funciones para la consola y puertos de entrada salida 11. El preprocesador de C 12. Estructuras dinamicas de datos 13. Algoritmos recursivos, de ordenaci6n y de busqueda 14. Manejo de la memoria 15. Compilar y enlazar 16. Librerias y utilidades del compilador 17. Rutinas en lenguaje ensamblador 18. Comunicaciones. Servicios del DOS y del BIOS 19. C y DOS 20. Control de procesos 21. Grcificos con C 22. Representaciones grcificas 23. Utilizaci6n del PWB 24. Instalaci6n de Microsoft C A. Ficheros .h de C B. C6digos de caracteres (ASCII) C. Indice alfabetico bles IBM. Este compilador ademas de correr bajo MSDOS y OS/2, inclu- ye tambien soporte para Microsoft Windows. Este libro posee varias caracteristicas dignas de resaltar. Es breve en teoria y abundante en ejemplos, 10 que Ie hara aun mas facil: el aprendiza- je para aquellas personas que quieren iniciarse en el tema, 0 la busqueda y compresi6n de un tema puntual para aquellas otras personas entendidas en programaci6n C. La metodologia utilizada en el desarrollo de los pro- gramas es la descomposici6n arriba-abajo (top down). Es un libro facil de entender. La materia total que compone la Enciclopedia dellenguaje C, se ha dividido en los siguientes capitulos y apendices:
  • 32. Todo esto se ha documentado con alrededor de 175PROBLEMAS RE- SUELTOS, utilizando la programaci6n estructurada, muchos de ellos vali- dos como parte integrante en el desarrollo de aplicaciones. He recibido ayuda de algunas personas durante la preparaci6n de este libro, y por ello estoy francamente agradecido. En especial quiero expresar mi agradecimiento a la firma Microsoft, por la ayuda material que me ha prestado.
  • 33. PARTE 1 Programaci6n con el Lenguaje C • Introducci6n al Lenguaje C • Elementos del Lenguaje C • Comenzando con el Lenguaje C • Sentencias de Control • Tipos Estructurados de Datos • Punteros • Funciones
  • 34. EI C es un lenguaje de programaci6n de prop6sito general. Sus principales caracteristicas son: - Programaci6n estructurada. - Economia en las expresiones. - Abundancia en operadores y tipos de datos. Codificaci6n en alto y bajo nivel simultaneamente. Reemplaza ventajosamente la programaci6n en ensamblador. Utilizaci6n natural de las funciones primitivas del sistema. No esta orientado a ningun area en especial. Producci6n de c6digo objeto altamente optimizado. - Facilidad de aprendizaje. Ellenguaje C naci6 en los Laboratorios Bell de AT&Ty ha sido estre- chamente asociado con el sistema operativo UNIX, ya que su desarrollo se realiz6 en este sistema y debido a que tanto UNIX como el propio com- pilador C y la casi totalidad de los programas y herramientas de UNIX, fueron escritos en C. Su eficiencia y claridad han hecho que el lenguaje ensamblador apenas haya sido utilizado en UNIX.
  • 35. Este lenguaje esta inspirado en ellenguaje B escrito por Ken Thomp- son en 1970con intenci6n de recodificar el UNIX, que en la fase de arran- que estaba escrito en ensamblador, en vistas a su transportabilidad a otras maquinas. B era un lenguaje evolucionado e independiente de la maquina, inspirado en ellenguaje BCPL concebido por Martin Richard en 1967. En 1972,Dennis Ritchie, toma el relevoy modifica ellenguaje B, crean- do ellenguaje C y reescribiendo el UNIX en dicho lenguaje. La novedad que proporcion6 ellenguaje C sobre el B fue el disefio de tipos y estructu- ras de datos. Los tipos basicos de datos eran char (caracter), int (entero),jloat (reales en simple precisi6n) y double (reales en doble precisi6n). Posteriormente se afiadieron los tipos short (enteros de longitud ~ longitud de un int), long (enteros de longitud ~ longitud de un int), unsigned (enteros sin sig- no) y enumeraciones. Los tipos estructurados basicos de C son las estruc- turas, las uniones y los arrays. Estos permiten la definici6n y declaraci6n de tipos derivados de mayor complejidad. Las instrucciones de control de flujo de C son las habituales de la pro- gramaci6n estructurada: if, for, while, switch-case, todas incluidas en su predecesor BCPL. C incluye tambien punteros y funciones. Los argumentos de las fun- ciones se pasan por valor, esto es copiando su valor, 10 cual hace que no se modifiquen los valores de los argumentos en la Hamada. Cuando se de- sea modificar los argumentos en la Hamada, estos se pasan por referencia, es decir, se pasan las direcciones de los argumentos. Por otra parte, cual- quier funci6n puede ser Hamada recursivamente. Una de las peculiaridades de C es su riqueza de operadores. Puede decirse que practicamente dispone de un operador para cada una de las posibles operaciones en c6digo maquina. Hay toda una serie de operaciones que pueden hacerse con ellenguaje C, que realmente no estan incluidas en el compilador propiamente dicho, sino que las realiza un preprocesador justa antes de cada compilaci6n. Las dos mas importantes son # define (directriz de sustituci6n simb6lica 0 de definici6n) e # include (directriz de inclusi6n en e1 fichero fuente).
  • 36. Finalmente, C, que ha sido pensado para ser altamente transportable y para programar 10improgramable, igual que otros lenguajes tiene sus in- convenientes. Carece de instrucciones de entrada/salida, de instrucciones para manejo de cadenas de caracteres, con 10 que este trabajo queda para la libreria de rutinas, con la consiguiente perdida de transportabilidad. La excesivalibertad en la escritura de los programas puede llevar a errores en la programaci6n que, por ser correctos sintacticamente no se detectan a simple vista. Por otra parte las precedencias de los operadores convierten a veces las expresiones en pequenos rompecabezas. A pesar de todo, C ha demostrado ser un lenguaje extremadamente eficaz y expresivo. Este lenguaje ha evolucionado paralelamente a UNIX, que a su vez ha pasado por diversas versiones entre las que destaca la de Microsoft con su XENIX para micros de 16 bits. En este apartado se van a exponer los pasos a seguir en la realizaci6n de un programa, por medio de un ejemplo. La siguiente figura, representa es- tos pasos en el orden en el que hay que ejecutarlos. El ejemplo de la figura indica que una vez editados los ficheros fuente a.c y b.c, son compilados obteniendose los ficheros objeto a.obj y b.obj los cuales son enlazados con el fichero c.obj, con la libreria d.lib y con las li- brerias del sistema .lib dando lugar a un unico fichero ejecutable a.exe. La orden correspondiente para compilar y enlazar los ficheros expuestos en este ejemplo, es la siguiente: Para ejecutar el fichero a.exe resultante, escribir el nombre de dicho fichero (a), y pulsar Enter.
  • 37. C 0 E a.c M a.obj D P I I T L 0 A D R b.c 0 b.obj R E N L c.obj A a.exe Z A D 0 d.lib R Para editar un programa, primeramente llamaremos, para su ejecucion, al programa editor 0 procesador de textos que vayamos a utilizar. Podemos utilizar el procesador de textos suministrado con el compilador 0 nuestro propio procesador (ver capitulo 23). El nombre del fichero para salvar el programa en el disco, debe tener como extension .c. El paso siguiente, es escribir el texto correspondiente al program a fuen- te. Cada sentencia dellenguaje C finaliza con un punto y coma y cada li- nea del programa la finalizamos pulsando la tecla Enter. Como ejercicio para practicar 10 hasta ahora expuesto, escribir el si- guiente ejemplo:
  • 38. CAPITULO I: INTRODUCCION AL LENGUAJE C 43 # include <stdio.h > # include <stdlib.h> main( ) ( char *mensajel char *mensaje2 char nombre[50]; HBienvenido a C':· HTe alegrard el haberme conocido"; system(Hcls"); printft';,Cudl es tu nombre? "); gets(nombre); printj("n%s %sn%sn': mensajel, nombre, mensaje2); J Comentamos brevemente cada linea de este programa. No apurarse si al- gunos de los terminos no quedan muy claros ya que todo ellos se venin con detalle en capitulos posteriores. Las dos primeras lineas incluyen las declaraciones necesarias para las funciones que aparecen en el programa. Estas funciones son: system( ), printf( ) y gets( ). A continuaci6n se escribe la funci6n principal main( ). Las dos pri- meras Hneas de esta, definen las cadenas de caracteres mensajel y mensaje2 y la siguiente linea define la cadena de caracteres nombre para contener 49 caracteres mas el caracter de "fin de cadena" que aftade C automati- camente. La funci6n printj( ) escribe el contenido de las variables especificadas en la misma. La funci6n gets( ) permite introducir datos a traves del teclado para la variable especificada, en este caso nombre.
  • 39. El programa editado esta ahora en la memoria. Para que este trabajo pue- da tener contir.uidad, el programa escrito se debe grabar en el disco utili- zando la orden correspondiente del editor. El siguiente paso es compilar el programa, esto es, traducir el programa fuente a lenguaje maquina para posteriormente enlazarlo con las librerias de C y obtener as! un programa ejecutable. Estas operaciones, compilar y enlazar, se efectuan mediante la orden c/. Al compilar un programa, se nos pueden presentar errores de compi- lacion, debidos a que el programa escrito no se adapta a la sintaxis y reglas del compilador. Estos errores se iran corrigiendo hasta obtener una com- pilacion sin errores. Cada vez que se realiza el proceso de compilacion y enlace del programa actual, C genera automaticamente sobre el disco un fichero con extension .exe. Este fichero puede ser ejecutado directamente desde el DOS, sin el soporte de C, escribiendo el nombre del fichero .exe despues del prompt del DOS y pulsando Enter a continuacion. Cuando se crea un fichero ejecutable, C utiliza primero el compilador para compilar el programa fuente, dando lugar a un fichero intermedio co- nocido como fichero objeto (.obj). A continuacion C utiliza el programa Iink.exe para unir, en un unico fichero ejecutable, el modulo 0 los modulos del programa compilados separadamente y las rutinas de las librerias del compilador C que el programa necesite.
  • 40. Al ejecutar el programa, pueden ocurrir errores durante la ejecucion. Por ejemplo, puede darse una division par cero. Estos errores solamente pueden ser detectados por C cuando se ejecuta el programa y senin notifi- cados con el correspondiente mensaje de error. Hay otro tipo de errores que no dan lugar a mensaje alguno. Por ejem- plo: un programa que no termine nunca de ejecutarse, debido a que pre- senta un lazo, donde no se llega a dar la condicion de terminacion. Para detener la ejecucion se tienen que pulsar las teclas Ctrl +C. Un programa una vez ejecutado puede dar lugar a una solucion incorrec- ta. Este caso exige un analisis minucioso de como se desarrolla el progra- ma en su ejecucion; esto es, hay que entrar en la fase de depuracion del programa. La forma mas sencilla y eficaz para realizar este proceso, es utilizar un programa depurador. En el capitulo 16, se explica como utilizar el de- purador Code View de Microsoft. Vamos a preparar un programa formado par un solo modulo fuente, para depurarlo. El primer paso sera editar el programa. Como ejemplo escribit el siguiente programa, e1cual, imprime el valor y su cuadrado, de cada uno de los elementos de una matriz numerica de 5 filas y 3 columnas, y cuenta e imprime el numero de elementos que son pares. #include <stdio.h> #define FILAS 5 #define COLS 3
  • 41. void display( iot n ) [ iot cuadrado; cuadrado = n *n; printj("el cuadrado de %2d es %3d n': n, cuadrado); J iot numero_par( iot x ) [ if (x % 2 = = 0) retu rn (1); else return (0); main( ) [ static iot a[FILAS][COLS] = [1,2,3,4,5,6, 7,8,9,10,11,12,13,14,15 j; iot fila, columna; for (fila = 0; fila < FILAS; fila + +) for (columna = 0; columna < COLS; columna + +) [ display( aUila][columna] ); if ( numero~ar(aUila][columna]) ) numeros~ares+ +; J printj(" n nTotal numeros pares: %d n': numeros_pares); j Una vez editado salvamos el programa en el disco. Llamemosle por ejemplo progOl02.c.
  • 42. Fijandonos en la funci6n principal, main(), vemos que definimos una ma- triz cuadrada cca': para a continuaci6n recorrerla elemento a elemento por filas. Esta funci6n principal llama a la funci6n display( ) que escribe el va- lor del elemento objeto de analisis y su cuadrado, y llama tambien a la fun- cion numero_par( ) que indica si el numero es 0 no par. En el caso de que sea par incrementamos en una unidad el contador numeros_pares. Una vez que se han analizado todos los elementos de la matriz, se es- cribe el numero de valores pares encontrados. Como siguiente paso, compilaremos el programa con las opciones IZi y IOd. La opcion IZi hace que se incluya en el fichero ejecutable resultante, informacion necesaria para realizar la depuracion y la opcion IOd impide la optimizacion, la cual puede dificultar la depuracion. Cuando finaliza el proceso de compilacion y enlace, invocamos al depura- dor (debug). Las operaciones minimas que debe incluir un depurador son las si- guientes:
  • 43. Permite ver la sentencia del programa que es ejecutada. Code View incluye las siguientes opciones: Ejecutar una sentencia cada vez, incluidas funciones definidas por el usuario. Esta modalidad se activa y se continua, pulsan- do la tecla F8. Si no queremos que las funciones se ejecuten sen- tencia a sentencia pero sf la funci6n principal main( ), utilizar la tecla FIO. Si pulsamos la tecla F5, la ejecuci6n continua hasta el final del programa 0 hasta el primer punto de parada, si este existe. Un punto de parada es una pausa que se hace en un lugar determi- nado dentro del programa. Esto permite testear los valores de las variables en ese instante. Colocar los puntos de parada donde se sos- peche que esta el error. Para poner 0 quitar una pausa, se coloca el cursor en ellugar don- de va a tener lugar la pausa y se pulsa F9. Las expresiones de seguimiento permiten observar los valores, de las variables 0 de expresiones del programa, mientras este se ejecuta (6r- denes Add Watch, Delete Watcha, ... ). Ejecutar la orden Add Watch ... y escribir en el recuadro corres- pondiente, fila. Realizar la misma operaci6n para incluir en la ventana de seguimiento columna y aUilaJ[columnaj. Continuar la ejecuci6n pulsando F8 0 FIO. Si se pulsa la tecla F5, la ejecuci6n del programa continua hasta el final del pro- grama 0 hasta un punto de parada si se encuentra.
  • 44. El nombre de un fichero consta de dos partes: el nombre base que puede tener hasta ocho caracteres y la extension que puede tener hasta tres carac- teres y va separada del nombre base por un punto. C identifica las siguien- tes extensiones con los ficheros que a continuaci6n se indican: .obj fichero resultante de la compilaci6n de un fichero fuente. No es ejecutable . .mak fichero que contiene una lista de m6dulos y las acciones que con ellos debe hacerse para construir el programa final. Cuando se especifica el nombre de un fichero sin extensi6n, C asume por defecto la extensi6n .obj.
  • 45. Las palabras clave aparecenln en negra y deben escribirse exactamente como aparecen. EI texto que no aparece en negra, significa que ahi debe ponerse la informacion indicada por ese texto. Los puntos suspensivos "..." indican que pueden aparecer mas elementos de la misma forma. Cuando dos 0 mas opciones aparecen entre Haves "{ ]" separadas por "I", se elige una, la necesaria dentro de la sentencia.
  • 46. Estos caracteres son utilizados para formar las constantes, los identi- ficadores y las palabras clave de C. El compilador C trata las letras mayusculas y minusculas como carac- teres diferentes. Por ejemplo los identificadores Pi y PI son diferentes. Espacio en blanco, tabulador horizontal (HT), tabulador vertical (VT), avan- ce de pagina (FF), y nueva linea (LF 0 CR + LF) son caracteres denomina- dos espacios en blanco, porque la labor que desempefian es la misma que la del espacio en blanco, esto es, actuar como separadores entre los ele- mentos de un programa. Los espacios en blanco en exceso son ignorados por el compilador, 10 cual nos permite escribir programas mas legibles. EI caracler Ctr! +Z bajo DOS, (equivalente a Ctrl +0 bajo UNIX) es tratado POl' el compiIador como un indicador de fin de fichero (End Of File).
  • 47. Los caracteres tambien pueden ser representados por secuencias de escape. Una secuencia de escape esta formada por el caracter seguido de una letra 0 de una combinaci6n de digitos. Son utilizadas para acciones como nueva linea, tabular y para representar caracteres no imprimibles. n t v b r f a ' Nueva linea Tab horizontal Tab vertical (s610 para impresora) Backspace (retroceso) Retorno de carro Alimentaci6n de pagina (s610 para impresora) Bell (alerta, pitido) Comilla simple Comilla doble Backslash (barra invertida) Canicter ASCII. Representaci6n octal Caracter ASCII. Representaci6n hexadecimal ddd xdd
  • 48. Hay varios tipos fundamentales de datos. Los ficheros de cabecera Iimits.h y f1oat.h especifican los valores maximo y minima para cad a tipo. Los po- demos clasificar en: Tipos enteros: char, short, int, long y enum. Tipos reales: float, double y long double. Otros: void. Cada tipo entero puede ser calificado por las palabras clave signed 0 unsigned, 10 que da lugar a tener disponibles los siguientes tipos extras: signed char, unsigned char signed short, unsigned short signed int, unsigned int signed long, unsigned long Un entero calificado signed es un entero con signo, esto es, un ntlme- ro entero positivo 0 negativo. Un numero entero calificado unsigned es un numero entero sin signo, el cual es manipulado como un numero entero positivo. Si los calificadores signed y unsigned se utilizan sin un tipo especffi- co, se asume el tipo into Por este motivo, las siguientes declaraciones son equivalentes: signed x; signed int X; unsigned y; unsigned int y; El tipo char es utilizado para almacenar un valor entero en el rango -128 a 127, correspondiente a un caracter del c6digo ASCII. Solamente los va- lores 0 a 127 son equivalentes a un caracter.
  • 49. De forma similar el tipo unsigned char puede almacenar valores en el rango de 0 a 255, valores correspondientes a los numeros ordinales de 10s 256 caracteres ASCII. Este ejemplo declara una variable car de tipo char, capaz de contener un canicter cuyo c6digo ASCII se correspondeni con un valor entero entre o y 127. Otros ejemplos son: char a = 'z'; signed char b = Ox07; unsigned char c = 32; De forma similar el tipo unsigned short puede almacenar valores en el rango de 0 a 65535 (0 a 2EI6-1). Este ejemplo declara i y j, como variables enteras con posibilidad de tDmar vaDres entre -'3216'il y '1'2161. Otros ejempos son: short int a = -500; signed short b = 1990; unsigned short int c = OxfOOO;
  • 50. Un entero es para C un numero sin punta decimal. El rango de valores de- pende de la maquina. Igualmente ocurre con el tipo unsigned into Para una maquina con un procesador de 16 bits el rango de valores es de: -32768 a 32767 (-2EI5 a 2EI5-1) para el tipo into o a 65535 ( 0 a 2EI6-1) para el tipo unsigmld. El uso de enteros produce un c6digo compacta y rapido. Para una ma- quina de 16 bits este tipo es equivalente al tipo short y solamente oc4pa 2 bytes de memoria. En general: Este ejemplo declara las variables n y x de tipo entero. Otros ejemplos son: int a = 2000; signed int b = -30; unsigned int c = Oxf003; Este tipo de numeros es id6neo para aplicaciones de gesti6n. Al igual que los enteros, son numeros sin punta decimal comprendidos en el rango de: -2147483648 a 2147483647 (-2E31 a 2E31-1) para el tipo long. o a 4294967295 (0 a 2E32-1) para el tipo unsigned long.
  • 51. Este ejemplo declara las variables n y m de tipo entero, pudiendo to- mar valores entre -2147483648 y 2147483647. Otros ejemplos son: long a = -IL; signed long b = 125; unsigned long int c = Oxlj00230j; La declaraci6n de un tipo enumerado es simplemente una lista de valores que pueden ser tornados por una variable de ese tipo. Los valores del tipo enumerado se representanin con identificadores, que senin las constantes del nuevo tipo. tunes, martes, miercotes, jueves, viernes, sabado, domingo hoy;
  • 52. Este ejemplo declara las variables hoy y ayer del tipo enumerado dia----semana. Estas variables pueden tomar cualquier valor de los especi- ficados, lunes ... domingo. El valor ordinal de lunes es O. Los elementos que aparecen enumerados en la lista son considerados como constantes enteras. Crear una enumeraci6n es definir un nuevo tipo de datos, denominado tipo enumerado y declarar una variable de este tipo. La sintaxis es la siguiente: enum tipo_enumerado ! Despues de definir un tipo enumerado, podemos declarar una 0 m,b variables de ese tipo, de la forma: }; enum colores color; Este ejemplo declara una variable color del tipo enumerado colores, la cual puede tomar cualquier valor de los especificados en la lista.
  • 53. Cada identificador, de la lista de constantes enteras en una enumera- ci6n, tiene asociado un valor. Por defecto, el primer identificador tiene aso- ciado el valor 0, el siguiente el valor 1, y as! sucesivamente. A cualquier identificador de la lista, se Ie puede asignar un valor ini- cial por medio de una expresi6n con stante. Los identificadores sucesivos tomanln valores correlativos a partir de este. azul, amarillo, raja, verde color; Este ejemplo define un tipo enumerado Hamado colores y declara una variable color de ese tipo. Los valores asociados a los identificadores son los siguientes: azul = 0, amarillo =1, raja =2, verde = 0, blanco = 1 Y negro = 2. 3. Desafortunadamente, no es posible leer 0 escribir directamente un valor de un tipo enumerado. Estos numeros son los mas recurridos en un lenguaje de programaci6n. Un real en simple precision es un numero que puede tener un punta decimal y que puede estar comprendido en el rango de:
  • 54. -3.402823E + 38 a -1.175494E-38 para numeros negativos 1.175494E-38 a 3.402823E+38 para numeros positivos Un numero real en simple precision no tiene mas de 7 digitos signifi- cativos. Este ejemplo declara la variable x de tipo real en simple precision. Otros ejemplos son: float a = 3.14159; float b = 2.2e-5; Un numero real en doble precision es un numero que puede tener un punta decimal y puede estar comprendido en el range de: -1.79769E+308 a -2.22507E-308 para numeros negativos 2.22507E-308 a 1.79769E+308 para numeros positivos Un numero real en doble precision tiene hasta 16 digitos significati- vos. Esto da lugar a calculos mas exactos que en simple precision. Este ejemplo declara la variable x de tipo real en doble precision. Otro~ ejemplos son: double a = 3.1415926; double b = 2.2e-8;
  • 55. -1.189731£+4932 a -3.362103E-4932 para numeros negativos 3362103E-4932 a 1.189731£+4932 para numeros positivos Un numero real en doble precision formato largo no tiene mas de 19 digitos significativos. Esto da lugar a calculos mas precisos que en doblc precision. long double X; long double y = 3.17e+425; El tipo void se utiliza para dec1arar funciones que no retornan un valor o para dec1arar un puntero a un tipo no especificado. Si void aparece entre parentesis a continuacion del nombre de una funcion, no es interpretado como un tipo. En este caso indica que la funcion no acepta argumentos. double jx(void); void jy(void); void *P; Este ejemplo dec1ara la funcion denominada jx, como una funcion sin argumentos que devue1ve un valor de tipo real de doble precision; la funcionjy, como una fundon sin argumentos que no devuelve valor algu- no; y un puntero P a un objeto de un tipo no espedficado. Los tipos derivados son construidos a partir de los tipos fundamentales. Algunos de ellos son los siguientes:
  • 56. Un puntero es una direcci6n de memoria que indica d6nde se localiza un objeto de un tipo especificado. Para definir una variable de tipo puntero se utiliza el operador de indirecci6n *. int *p,' char *plineas[40j,' Este ejemplo declara un puntero p a un valor entero; y un array de punteros plineas (plineas[Oj a plineas[39J) a valores de tipo char. Una estructura es una variable que representa 10 que normalmente cono- cemos como registro, esto es, un conjunto de uno 0 mas campos de igual o diferentes tipos. float a, b; complejo; struct persona { char nombre[20j; char apellidos[40j; long dni; Este ejemplo declara las variables complejo y reg, como estructuras o registros. La variable complejo comprende los campos a y b de tipo real;
  • 57. y la variable reg comprende los campos nombre y ape/lidos que son cade- nas de caracteres y el campo dni de tipo long. Una union tiene la misma forma de definici6n que una estructura. Las unio- nes, a diferencia de las estructuras, representan registros variables. Esto cjuiert: decir que una variable de este tipo, puede alternar entre varios tip os. Un array es un conjunto de objetos, todos del mismo tipo, que ocupan po- siciones sucesivas en memoria. Para definir un array se utiliza el operador [ ] despues del nombre del array. Este ejemplo declara un array !ista de 40 elementos (!ista[O]a !ista[39J) para almacenar valores enteros. Una funci6n es un subprograma C, el cual toma argumentos de unos tipos dados y retorna un valor de un tipo especificado. Para declarar una fun- ci6n se utiliza el operador ( ) despues del nombre de la funci6n. Permite declarar nuevos nombres de tipos de datos; esto es, sin6nimos de otms tipos ya sean fundamentales 0 derivados, los cuales pueden ser utili- zados mas tarde para declarar variables de esos tipos.
  • 58. typedef int ENTERO; typedef int (*PFlj( ); typedef struct persona REG; Este ejemplo propone el tipo ENTERO como sin6nimo de int; el tipo PFI como un puntero a una funci6n que devuelve un valor entero; y el tipo REG como sin6nimo de struct persona. ENTERO n; PFI p; REG per; declaran n como una variable de tipo entero, p como un puntero a una funci6n que devuelve un valor entero y per como una estructura 0 registro de tipo persona. Las declaraciones typedej permiten parametrizar un programa para evitar problemas de portabilidad. Utilizando typedej con los tipos que pue- den depender de la instalaci6n, cuando se lleve el programa a otra instala- ci6n s610 se tendnin que cambiar estas declaraciones. Una constante es un valor que, una vez fijado por el compilador, no cam- bia durante la ejecuci6n del programa. Una constante en C puede ser un mimero, un canicter 0 una cadena de caracteres.
  • 59. En general, si la constante es positiva, el signa + es opcional y si es negativa, lleva el signo -. El tipo de una constante entera viene determina- do por su valor. Tambien se puede indicar explicitamente el tipo de una constante entera, afiadiendo los sufijos L, U, 0 UL (mayusculas 0 minus- culas). Si el sufijo es L, su tipo es long cuando el valor puede ser represen- tado en este tipo, si no es unsigned long. Si el sufijo es U, su tipo es unsig- ned int cuando el valor puede ser representado en este tipo, si no es unsigned long. Si el sufijo es UL, su tipo es unsigned long. 1522U 1000L 325UL constante entera de tipo unsigned int constante entera de tipo long constante entera de tipo unsigned long Una constante decimal puede tener uno 0 mas digitos, 0 a 9, de los cuales el primera de ellos es distinto de cero. 4326 432600 constante entera de tipo int constante entera de tipo long Una constante octal puede tener lomas digitos, 0 a 7, precedidos por o (cera). Su valor esta comprendido en el rango: Oa077777 0100000 a 0177777 0200000 a 017777777777 020000000000 a 037777777777 para constantes tipo int para constantes tipo unsigned int para constantes tipo long para constantes tipo unsigned long Una constante hexadecimal puede tener lomas caracteres, 0 a 9 y A a F, precedidos por Ox 0 OX (cero mas x). Su valor esta comprendido en el rango:
  • 60. OxOa Ox7FFF Ox8000 a OxFFFF OxlOOOOa Ox7FFFFFFF Ox80000000 a OxFFFFFFFF para constantes tipo int para constantes tipo unsigned int para constantes tipo long para constantes tipo unsigned long 256 0400 OxJOO -0400 -OxJOO especifica el nO 256 en decimal especifica el n° 256 en octal especifica el nO 256 en hexadecimal especifica el nO -256 en octal especifica el nO -256 en hexadecimal Una constante real esta formada por una parte entera, seguida por un punto decimal, y una parte fraccionaria. Tambien se permite la notaci6n cientffi- ca, en cuyo caso se afiade al valor una e 0 E, seguida por un exponente positivo 0 negativo. donde dfgitos representa cero 0 mas digitos del 0 al 9 y E 0 e es el simbolo de exponente de la base 10 que puede ser positivo 0 negativo (2E-5 = 2 x J(f5). Si la constante real es positiva no es necesario especificar el sig- no y si es negativa lleva el signa menos (-). -17.24 17.244283 .008e3 27E-3 Una constante real tiene siempre tipo double, a no ser que se afiada a la misma una f 0 F, en cuyo caso sera de tipo float, 0 una I 0 L para indicar que es de tipo long double.
  • 61. Este tipo de constantes esta formado por un unico caracter encerrado en- tre comillas simples. Una secuencia de escape es considerada como un uni- co canicter. espacio en blanco letra minuscula x nueva linea canicter ASCII Ese ' n' , x1B' Una constante de caracteres es una cadena de caracteres encerrados entre comillas dobles. "Esto es una eonstante de earaeteres" "3.1415926 " "Paseo Pereda 10, Santander" En el ejemplo siguiente el car<icter n fuerza a que la cadena "0 pul- sar Enter" se escriba en una nueva linea. Cuando una cadena de caracteres es demasiado larga puede utilizarse el canicter " " como canicter de continuaci6n.
  • 62. HEsta cadena de caracteres es dema siado larga." Dos 0 mas cadenas separadas por un espacio en blanco sedan conca- tenadas en una sola cadena. printf(HPrimera cadena," H segunda cadena "); Los caracteres de una cadena de caracteres son almacenados en locali- zaciones sucesivasde memoria. Cada cadena de caracteres es finalizada auto- m<iticamente por el caracter nulo representado por la secuencia de escape o. El tipo de una cadena de caracteres es el tipo array donde cada ele- mento es de tipo char (char [ J) y la clase de almacenamiento es static. El numero de elementos de un array, necesarios para almacenar una cadena de caracteres, es igual al numero de caracteres de la cadena, mas uno para el caracter nulo de terminaci6n. Los identificadores son nombres dados a constantes, variables, tipos, fun- ciones y etiquetas de un programa. La sintaxis para formar un identifica- dor es la siguiente:
  • 63. 10 cual indica que un identificador consta de uno 0 mas caracteres (letras, digitos y el caracter de subrayado) y que el primer canicter debe ser una tetra 0 el cardcter de subrayado. Las letras pueden ser mayusculas 0 minusculas y se consideran como caracteres diferentes; esto es, los identificadores Suma, suma y SUMA son diferentes. Los identificadores pueden tener cualquier numero de caracteres pero solamente los 31 caracteres primeros, son significativos. Esto quiere decir que un identificador es distinto de otro cuando difieren al menos en uno de los 31 primeros caracteres. suma Catcuto~umeros~rimos _ordenar ab123 Las palabras clave son identificadores predefinidos que tienen un signifi- cado especial para el compilador C. Un identificador definido por el usua- rio, no puede tener el mismo nombre que una palabra clave. auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Ademas de las palabras clave anteriores, el compilador C de Micro- soft tiene tambi(~n las siguientes:
  • 64. _asm _based _cdecl _emit _export ~ar ~astcall ~ortran ----huge ~nterrupt ~oadds _near _pascal -3averegs _segment -3egname -3elf Un comentario es una secuencia de caracteres utilizada para explicar el co- digo fuente. Microsoft C soporta comentarios estilo C y estilo C++. Un comentario estilo C es una secuencia de caracteres cualesquiera en- cerrados entre los simbolos 1* y *1. Estos comentarios pueden ocupar mas de una linea, pero no pueden anidarse. Por ejemplo: 1* Este es un comentario * que ocupa varias * !ineas. *1 Un comentario estilo C+ + comienza con los caracteres II y termina al final de la linea. Estos comentarios no pueden ocupar mas de una linea. Por ejemplo: Un comentario puede aparecer en cualquier lugar donde se permita aparecer un espacio en blanco. El compilador trata un comentario como a un espacio en blanco. El valor de una variable, a diferencia de las constantes, puede cambiar a 10 largo de la ejecucion de un programa.
  • 65. La sintaxis correspondiente a la declaraci6n de una variable es la si- guiente: c1ase representa una de las cuatro clases siguientes: auto, register, sta- tic, 0 extern. La clase de una variable determina si esta tiene ca- racter global (static 0 extern) 0 local (auto 0 register). Una variable declarada fuera de todo bloque (conjunto de sentencias encerradas entre ( }) es, por defecto, global y es accesible en el resto del archivo fuente en el que esta declarada. Por el contrario, una variable de- clarada dentro de un bloque, es por defecto local y es accesible solamente dentro de este. Cada variable de un programa, debe declararse antes de ser utilizada. La declaraci6n consiste en enunciar el nombre de la variable y asociarle un tipo. El tipo determina los valores que puede tomar la variable asi como las operaciones que con ella pueden realizarse. iot suma, incremento; char car, linea/80]; char car = ( 0'; / * car igual al cardcter nulo */ iot c = 1; / * inicializar c a 1*/
  • 66. Ala declaraci6n de un objeto, se puede anteponer el calificador eonst, con el fin de hacer que dicho objeto sea, en lugar de una variable, una constante. const int k = 12; const int v[ j = [1, 2, 3, 4j; A un objeto declarado como una constante no se Ie puede asignar un valor. Por ello, al declararlo debe ser inicializado. Si k ha sido declarado como constante, las siguientes sentencias darian lugar a un error: k = 100; k+ +; / * error */ / * error */ Una declaraci6n de un puntero precedida por eonst, hace que el obje- to apuntado sea una constante, no sucediendo 10 mismo con el puntero. const char *pe = "abed"; pe[Oj = 'z'; / * error */ pe = "efg"; / * eorreeto */ Si 10 que se pretende es declarar un puntero como una con stante, pro- cederemos asi: char *const pe = "abed"; pe[Oj = 'z'; / * eorreeto */ pe = "efg";' / * error */ Para hacer que tanto el puntero como el objeto apuntado sean cons- tantes, procederemos como se indica a continuaci6n: const char *const pe = "abed"; pe[Oj = 'z'; h error */ pe = "efg"; / * error */
  • 67. A la declaraci6n de un objeto, se puede anteponer el calificador volatile, con el fin de hacer que dicho objeto pueda ser modificado por otros pro- cesos diferentes al programa actual. Su utilizaci6n tiene sentido, por ejem- plo, en procesos concurrentes. Los calificadores const y volatile, pueden utilizarse conjuntamente 0 individualmente. Este ejemplo declara la variable v, entera (int), accesible desde cual- quier parte (extern), no modificable por el programa donde esta declarada (const), pero si modificable por otros procesos (volatile). Una expresi6n es una secuencia de operadores y operandos que especifi- can una operaci6n determinada. ++a suma+ =c cantidad * precio 7 * sqrt(a) - b / 2 Los operadores son simbolos que indican como son manipulados 10s da- tos. Se pueden clasificar en 10ssiguientes grupos: aritmeticos, 16gicos, re- lacionales, unitarios, 16gicospara manejo de bits, de asignaci6n, operador ternario para expresiones condicionales y otros.
  • 68. Division. Los operandos pueden ser enteros 0 reales. Si am- bos operandos son enteros el resultado es entero. En el res- to de los casos el resultado es real. Modulo 0 resto de una division entera. Los operandos tie- nen que ser enteros. int a = 10, b = 3, c; float x = 2.0, y; y=x+a; c=a/b; c=a%b; y=a/b; / * el resultado es 12.0 de tipo float */ / * el resultado es 3 de tipo int */ / * el resultado es 1de tipo int */ / * el resultado es 3 de tipo into Se convierte a float para asignarlo a y */ AND. Da como resultado el valor logico 1 si ambos ope- randos son distintos de cero. Si uno de ellos es cero el re- sultado es el valor logico O.Si el primer operando es igual acero, el segundo operando no es evaluado.
  • 69. OR. El resultado es 0 si ambos operandos son O. Si uno de los operandos tiene un valor distinto de 0, el resultado es 1. Si el primer operando es distinto de cero, el segundo ope- rando no es evaluado (ASCII 124). NOT. EI resultado es 0 si el operando tiene un valor distin- to de cero, y 1 en caso contrario. EI resultado es de tipo into Los operandos puedenser enteros, reales a punteros. p&& q p II q !p da como resultado 0 da como resultado 1 da como resultado 0
  • 70. Una expresi6n de Boole da como resuItado Ios vaIores I6gicos 0 0 1. Los operadores que intervienen en una expresi6n de Boole pueden ser: opera- dores 16gicos y operadores de relaci6n. int p, q; float x = 15, y p = x = = y; / * resultado p 0 */ q = (x < y) && (y < = z); /* resultado q 1 */ Cambia de signo aI operando (compIemento ados). EI ope- rando puede ser entero 0 real. CompIemento a 1.EI operando tiene que ser entero (canlc- ter ASCII 126).
  • 71. Los operandos para este tipo de operaciones tienen que ser de tipo en- tero (char, int, long, 0 enum), no pueden ser reales. a a & 0177; / *pone a cera todos los bits de a */ / * excepto los 7 bits de menor peso */ a a I m; / *pone a 1 todos los bits de a que */ / * estdn a 1 en m */ a a & -077; / *pone a alas 6 bits de menor peso de a */ En las operaciones de desplazamiento el primer operando es despla- zado tantas posiciones como indique el segundo. Si el desplazamiento es a izquierdas, se rellena con ceros por la derecha; si el desplazamiento es a derechas, se rellena con ceros por la izquierda si el operando es de tipo unsigned, en otro caso se rellena con el bit de signo. c=a«1; d=b»1; /* c = 6 */ /* d = -2 */
  • 72. En una operaci6n de asignaci6n, el valor de la derecha, es convertido al tipo del valor de la izquierda. x++; ++x; x --n; x = n--; i += 2; x *= n - 3 / * incrementa el valor de x en 1 */ / * incrementa el valor de x en 1 */ / * decrementa n en 1 y asigna el resultado a x */ / * asigna el valor de n a x y despues */ / * decrementa n en 1 */ / * realiza la operacion i = i + 2 */ h realiza la operacion x = x * (n-3) y no */ / * x = x * n - 3 */ / * realiza la operacion n = n > > 1 la cual des- */ / * plaza el contenido de n un bit a la derecha */
  • 73. C tiene un operador ternario (?:), que se utiliza en expresiones condiciona- les, las cuales tienen la forma: La expresi6n operandol debe ser de tipo entero, real 0 puntero. La eva- luaci6n se realiza de la siguiente forma: • Si el resultado de la evaluaci6n de operandol es distinta de 0, el re- sultado de la expresi6n condicional es operando2. • Si el resultado de la evaluaci6n de operandol es 0, el resultado de la expresi6n condicional es operando3. Este ejemplo dice que si a > b entonces mayor = a, en caso contra- rio, mayor = b. Un par de expresiones separadas por una coma son evaluadas de izquierda a derecha. Todos los efectos de la expresi6n de la izquierda son ejecutados antes de evaluar la expresi6n de la derecha, a continuaci6n el valor de la expresi6n de la izquierda es descartado. El tipo y el valor del resultado son el tipa y el valor del operando de la derecha. aux = vi, vi = v2, v2 = aux; for (a = 256, b = i; b < 512; a/=2, b *=2)
  • 74. Este operador accede a un valor indirectamente a traves de un puntero. El resultado es el valor direccionado por el operando. Este operador da la direcci6n de su operando. Este operador no se puede aplicar a un campo de bits perteneciente a una estructura 0 a un identifica- dor declarado con el calificador register. int *pa, b; int a[10}; / * pa es un puntero a un valor entero d / * a es una array de 10 elementos de tipo int ':'/ / * en el puntero pa se almacena la direcci6n */ / * del 7° elemento del array a ':'/ / * a b se Ie asigna el valor almacenado en la */ / * direcci6n especijicada por pa ':'/ Este operador da como resultado el tamafio en bytes de su operando 0 de un objeto del tipo especificado (se entiende por byte el espacio requerido para almacenar un canicter). El resultado es una constante de tipo size_t (un entero sin signo), el cual esta definido en el fichero <stdef.h >. La sintaxis es:
  • 75. donde expresi6n es un identificador 0 un nombre de un tipo de datos. Los parentesis son opcionales, excepto cuando la expresi6n se corresponde con un tipo de datos. "primera cadena': "segunda cadena': "tercera cadena" ]; const int cadenas = (size of cadena)/(sizeof cadena[Oj); En este ejemplo, cadena representa un array de punteros a objetos de tipo char. Puesto que el numero de elementos (punteros) no se ha espeeifi- cado, una forma faeil de calcularlo es mediante la operaci6n que se expre- sa a continuaci6n. El resultado se deposita en cadenas que ha side defini- da como una constante (const). La tabla que se presenta a continuaci6n, resume las reglas de priori dad y asociatividad de todos los operadores. Los operadores escritos sobre una misma linea tienen la misma prioridad. Las lineas se han colocado de ma- yor a menor prioridad. Una expresi6n entre parentesis, siempre se evalua primero. Los paren- tesistienen mayor prioridad y son evaluados de mas internos a mas externos.
  • 76. () [ ] -> * & ++ :> * / 070 + « » < <= > >= -- I-.- & II. Cuando los operandos dentro de una expresi6n son de tipos diferentes, se convierten a un tipo comun, de acuerdo con las reglas que se exponen a continuaci6n.
  • 77. Las reglas que se exponen, se aplican en ese orden, para cad a opera- cion bin aria perteneciente a una expresion, siguiendo el orden de evalua- cion expuesto anteriormente. 2. Si un operando es de tipo long double, el otro operando es con- vertido a tipo long double. 3. Si un operando es de tipo double, el otro operando es convertido a tipo double. 5. Cualquier operando de tipo unsigned char 0 unsigned short es con- vertido a tipo unsigned into 6. Si un operando es de tipo unsigned long, el otro operando es con- vertido a unsigned long. 7. Si un operando es de tipo long, el otro operando es convertido a tipo long. 8. Si un operando es de tipo unsigned int, el otro operando es con- vertido a tipo unsigned into long a; unsigned char b; int c; float d; int 1; Este ejemplo, teniendo en cuenta que primero se realiza la multiplica- cion, desputs la division y por ultimo la suma, se desarrollarfa de la forma siguiente:
  • 78. 2. c es convertido a unsigned int (paso 8). Se ejecuta la multiplica- ci6n (*) y se obtiene un resultado de tipo unsigned into 4. El resultado de b * c, es convertido a double (paso 3). Se ejecuta la divisi6n (I) y se obtiene un resultado de tipo double. 5. a es convertido a double (paso 3). Se ejecuta la suma (+) Yse ob'- tiene un resultado de tipo double. 6. El resultado de a + b * c / d, para ser asignado a 1, es pasado a entero por truncarniento, esto es, eliminando la parte fraccionaria. • Los operandos que intervienen en una determinada operaci6n, son convertidos al tipo del operando de precisi6n mas alta. • En una asignaci6n, el valor de la parte derecha es convertido al tipo del valor de la parte izquierda, de acuerdo con las siguientes reglas: Los caracteres se convierten a enteros con 0 sin extensi6n de sig- no, dependiendo esto de la instalaci6n. Bajo Microsoft C la con- versi6n se hace con extensi6n de signo. Los enteros se convierten a caracteres preservando los bits de me- nor peso, esto es desechando los bits de mayor peso en exceso. Los reales son convertidos a enteros, truncando la parte frac- cionaria.
  • 79. - Un double pas a a float, redondeando y perdiendo precisi6n si el valor double no puede ser representado exactamente como float. • Tambien ocurre conversi6n cuando un valor es pasado como argu- mento a una funci6n. Estas conversiones son ejecutadas indepen- dientemente sobre cad a argumento en la Hamada. En general, esto significa que un valor float es convertido a double, un valor chal o short es convertido a int y un valor unsigned char 0 unsigned shorl es convertido a unsigned into En C, esta permitida una conversion explicita del tipo de una expresi6n mediante una construcci6n denominada cast, que tiene la forma: La expresi6n es convertida al tipo especificado aplicando las reglas de conversi6n expuestas anteriormente. Por ejemplo, la funci6n raiz cuadrada (sqrt), espera como argumento un tipo double. Para evitar resultados inesperados en el caso de pasar un argumento de otro tipo, podemos escribir: Una variable de un determinado tipo, no siempre puede ser converti- da explicitamente a otro tipo. Por ejemplo: unsigned int a : 3; unsigned int b : 1; unsigned int c : 3; unsigned int d : 1; atributo; II bits 0 a 2 II bit 3 II bits 4 a 6 II bit 7
  • 80. La variable atributo es una estructura de longitud ocho bits. Si desea- mos copiar atributo en una variable atrib de tipo char, seguramente escri- biriamos: char atrib; atrib = (char}atributo; 10 cual da lugar a un error, ya que en general C no permite convertir una estructura a un tipo como char, aunque como en este caso, la longitudes de ambos tipos sean iguales. Utilizando conversiones explicitas de tipo sobre punteros, es posible convertir el valor de una variable de un determinado tipo a otro tipo cual- quiera. El formate general para llevar esto a la pnictica es: char *atrib; atrib = (char *}&atributo; define la variable a de tipo char cuyo contenido es el mismo que el de la estructura atributo. Algunas de las rutinas de las librerias de C, utilizan valores cuyos tipos son definidos en los ficheros .h. Algunos de estos tipos y sus definiciones, son los siguientes: c1ock_t este tipo esta definido en time.h y es utilizado por la fundon clock( ).
  • 81. este tipo esta definido en stdio.h y es utilizado por las funcio- nes jgetpos( ) y jsetpos( ). este tipo esta definido en stdio.h y en otros ficheros .h. Es un tipo entero sin signo, resultado del operador sizeo! este tipo esta definido en time.h y es utilizado por la funci6n timer ). FILE el tipo estructura FILE esta definido en stdio.h y es utilizado por las funciones estandar de entrada/salida.
  • 82. Un programa fuente C es una colecci6n de cualquier numero de directrices para el compilador, declaraciones, definiciones, expresiones, sentencias y funciones. Todo programa C debe contener una funci6n nombrada main(), don- de el programa comienza a ejecutarse. Las llaves ({ J) que incluyen el cuer- po de esta funci6n principal, definen el principio y el final del programa. Un programa C, ademas de la funci6n principal main(), consta gene- ralmente de otras funciones que definen rutinas con una funci6n especifi- ca en el programa. Esto quiere decir que la soluci6n de cualquier proble- ma, no debe considerarse inmediatamente en terminos de sentencias correspondientes a un lenguaje, sino de elementos naturales del problema mismo, abstraidos de alguna manera, que daran lugar al desarrollo de las funciones mencionadas. El disefio Top Down de programas, consiste precisamente en encon- trar la soluci6n de un problema mediante la aplicaci6n sistematica de des- composici6n del problema en subproblemas cada vez mas simples, aplicando la maxima de dividir para veneer.
  • 83. El empleo de esta tecnica de desarrollo de programas, as! como la uti- lizaci6n unicamente de estructuras secuenciales, alternativas y repetitivas, nos conduce a la denominada PROGRAMACION ESTRUCTURADA. Todos los ejercicios de esta obra senin desarrollados bajo el concepto de PROGRAMACION ESTRUCTURADA. Este ejemplo presenta una tabla de equivalencia entre grados Centl- grados y Fahrenheit de la forma siguiente: -30 C -24 C -22.00 F -11.20 F 90 C 96 C 194.00 F 204.80 F 1* Paso de grados Centigrados a Fahrenheit (F=915*C+32) * Directrices para el preprocesador (#) *1 # include Hstdio.h" 1* fichero estdndar de c: que se incluye en * el program a *1 I * Definicion de constantes *1 #define INF -30 1* limite inferior de la tabla de temperaturas *1 #define SUP 100 1* limite superior *1 1* Declaracion de funciones * (funcion prototipo 0 declaracion forward) *1
  • 84. main( ) / *Juncion principal - comienza el programa */ [ / *Declaracion de variables locales */ as, !>A. int centigrados; int incremento = 6; / * deJinicion e inicializacion */ centigrados = INF; / * sentencia de asignacion */ while (centigrados < = SUP) { / * Se llama a la Juncion y se Ie pasa un valor */ Jahrenheit = conversion(centigrados); printf("%10d C %10.2J F n': centigrados, Jahrenheit); centigrados + = incremento; } } / *Jin de la Juncion principal y del programa */ float conversion(int cent) { float Jhar; / * variable local conocida solamente aqul, en la Juncion */ / * los operandos son convertidos al tipo del operando de precision mas alta (float: 9.0 0 5.0) */ Jhar = 9.0 / 5.0 * cent + 32; return (fhar); / * retorna un valor a la sentencia de llamada */ } / * Fin de la Juncion de conversion */ La directriz # include "Jichero" Ie dice al compilador que incluya el fiche- ro especificado, en el programa fuente. Esto es necesario porque estos fi- cheros aportan, entre otras declaraciones, Ias funciones prototipo de Ias funciones de Ia Iibreria estandar que utilizamos en nuestros programas. Mediante Ia directriz # define identijicador valor se Ie indica al compila- dor, que toda aparici6n en el programa de identijicador, debe ser sustitui- da por valor.
  • 85. #include "stdio.h" h jichero estdndar de c; que se incluye e.n * el program a */ / * Dejinicion de constantes */ #dejine INF -30 h limite injerior de la tabla de temperaturas d #dejine SUP 100 / * limite superior */ Una declaraci6n introduce uno 0 mas nombres en el programa. Una decla- raci6n es una definici6n excepto: cuando declara una funci6n sin especifi- car el cuerpo de la misma, cuando contiene el calificador extern y no hay inicializaci6n, cuando la declaraci6n corresponde a un nombre de una es- tructura, 0 cuando es una declaraci6n typedej iot jx( iut x ) { return (x+ b); J struct complejo { float a, b; J
  • 86. extern int a; extern const b; int jx( int x ); struct complejo; typedef int ENTERO; Toda variable debe ser declarada antes de ser utilizada. En general, las variables no son inicializadas por C, pero si se desea, pueden ser inicia- lizadas en la propia declaracion. La definicion de una variable, declara la variable y ademas Ie asigna memoria; la definicion de una funcion, declara la funcion y ademas inclu- ye el cuerpo de la misma. int centigrados; int incremento = 6; float conversion (int cent) I float jahr; jahr = 9.0 / 5.0 * cent + 32; return (fahr); J La declaracion 0 la definicion de una variable, as! como la declara- cion de una funcion, pueden realizarse a nivel interno (dentro de la defini- cion de una funcion) 0 a nivel externo (fuera de toda definicion de fun- cion). La definicion de una funcion, siempre ocune a nivel externo. En el programa anterior, podemos observar las siguientes declaracio- nes y definiciones: / * declaracion de una juncion a nivel externo */ j * definicion a nivel externo */ / * definicion a nivel intern0 d float jahrenheit; float jahr;
  • 87. Una expresi6n es una combinaci6n de operadores y operandos que dan lu- gar a un unico valor. Una sentencia es la unidad ejecutable mas pequefia de un programa C. Las sentencias controlan el flujo u orden de ejecuci6n. Una sentencia C consta de una palabra clave (for, while, if ...else, etc.), expresiones, declaraciones, o llamadas a funciones. Dos 0 mas sentencias pueden aparecer sobre una misma linea, separa- das por punta y coma. Una sentencia compuesta 0 bloque, es una colecci6n de sentencias inclui- das entre Haves ({ D. Un bloque puede contener otros bloques. { jahrenheit = conversion(centigrados); printf(C<%10d C %10.2j F n': centigrados, jahrenheit); centigrados + = incremento; J
  • 88. Una funci6n es una colecci6n de sentencias que ejecutan una tarea especi- fica. Una funci6n no puede contener a otra funci6n. Puesto que las funciones son una herramienta muy valiosa en la cons- trucci6n de un programa C, vamos a anticiparnos a describir c6mo se de- claran y se definen, con el fin de poder utilizarlas desde el primer momen- ta en la programaci6n. Posteriormente se estudianin con mas detalle. La declaraci6n de una funci6n, tambien conocida como funcion prototi- po, consiste en: Se observa, que en la declarad6n de la fund6n, se dan sus caracteris- ticas pero no se define su contenido. Una fund6n puede ser declarada im- plicitamente 0 con una declaracion forward (fund6n prototipo). La declaracion implicita se da cuando la fund6n es llamada y no exis- te una declarad6n previa (declaraci6n forward). En este caso, C, por de- fecto, construye una fund6n prototipo con tipo de resuItado int y la lista de tipos de argumentos se construye, en base a los parametros formales espedficados en la definici6n de la fund6n. Esto obliga a que el tipo del resultado en la definici6n de la fund6n sea into La declaracion explicita, permite conocer las caracteristicas de la fun- ci6n antes de ser utilizada. La Iista de tipos de argumentos normal mente consiste en una lista de identificadores con sus tipos, separados par comas. En el caso de una fun- ci6n prototipo, se pueden omitir los identificadores, poniendo solamente
  • 89. los tipos. El ambito de validez de estos argumentos, queda restringido a la propia declaraci6n. float juncion--:x(int a, float b, char c); jloat juncion--:x(int, float, char); Para asegurar la portabilidad, se puede utilizar el tipo void para espe- cificar que una funci6n no acepta argumentos. float juncion_a(void); float juncion_a( ); Las funciones prototipo de las funciones pertenecientes a las librerfa~ estandar de C, como printf( ), son provistas por los ficheros de cabecera estandar (ficheros .h). La definici6n de una funci6n consta de una cabecera de funcion y del cuerpo de la fundon encerrado entre Haves. tipo-resultado nombre-funcion ([parametros jormalesJ) [ declaraciones de variables locales; sentencias; [return( expresion)J; 1 Las variables locales declaradas dentro del cuerpo de la funci6n, por definici6n solamente pueden utilizarse dentro del mismo.
  • 90. El tipo del resultado especifica que tipo de datos retorna la funci6n. Este, puede ser cualquier tipo fundamental, 0 tipo definido por el usuario, pero no puede ser un array 0 una funci6n. Por defecto, el valor retornado es into Este valor es devuelto a la sentencia de Hamada, por medio de la sentencia: Esta sentencia puede ser 0 no la ultima, y puede aparecer mas de una vez en el cuerpo de la funci6n. En el caso de que la funci6n no retorne un valor, se omite. Los panimetros formales de una funci6n son las variables que reciben los valores de los argumentos en la Hamada a la funci6n; consisten en una lista de identificadores con sus tipos, separados por comas. Para ejecutar una funci6n, hay que Hamarla. La Hamada a una funcion consta del nombre de la misma y de una lista de argumentos 0 valores a pasar denominados panimetros actuales, separados por comas yencerra- dos entre parentesis. / * Se llama a la funci6n pasando el valor cent(grados */ jahrenheit = conversion(centigrados); float conversion (int cent) / * cabecera de funci6n */ ! float fahr; jahr = 9.0 / 5.0 * cent + 32; return (fahr); l /*fin de la funci6n de conversi6n */ La cabecera de la funci6n tambien se podia haber escrito utilizando el estilo antiguo, de la siguiente forma:
  • 91. float conversion (cent) / * cabecera de fundon */ int cent; { Cuando se Hama a una funcion, el valor del primer panimetro actual es pasado al primer panimetro formal, el valor del segundo panimetro actual es pasado al segundo panimetro formal y as! sucesivamente. Todos los ar- gumentos, excepto los arrays, son pasados por valor. Esto es, a la funcion se pasa una copia del argumento, no su direccion. Esto hace que la fun- cion C, no pueda alterar los contenidos de las variables pasadas. En el ejemplo anterior, cuando la funcion conversion es Hamada, el panimetro formal cent recibe el valor del panimetro actual centigrados. Si se desea poder alterar los contenidos de los argumentos en la Ha- mada, entonces hay que pasarlos por referencia. Esto es, a la funcion, se pasa la direccion del argumento y no su valor por 10 que el panimetro for- mal correspondiente tiene que ser un puntero. Para pasar la direccion de un argumento, utilizaremos el operador &. main( ) { int a = 20, b = 30; intercambio(&a, &b); / * a y b son pasados por referenda */ printj(Ha es %d y b es %d n': a, b); l
  • 92. void intercambio(int 'i<X, int *y) [ *Y; z; / * contenido de x / * contenido de y contenido de y */ z */ En este ejemplo observamos que la funci6n intercambio tiene dos pa- rametros, x e y, de tipo "puntero a un entero", que reciben las direcciones de a y b respectivamente. Esto quiere decir que, al modificar el contenido de las direcciones x e y, indirectamente estamos modificando los valores de a y b respectivamente. Recordar la definici6n de puntero y las definicio- nes de los operadores de indireccion (*) y direccion-de (&). Un programa C puede ser dividido en uno 0 mas ficheros fuente. Un fiche- ro fuente C, es un fichero de texto que contiene todo 0 parte de un progra- ma C. Para compilar un programa formado por varios ficheros, se deben com- pilar por separado cada uno de los ficheros y, a continuaci6n, enlazarlos para formar un unico m6dulo ejecutable. Un fichero fuente puede contener cualquier combinaci6n de directri- ces para el compilador, deciaraciones y definiciones. Pero, un elemento como una funci6n 0 una estructura, no puede ser dividido entre dos ficheros fuen- tes. Por otra parte, un fichero fuente no necesita contener sentencias ejecu- tables; esto es, un fichero fuente puede estar formado, por ejemplo, sola- mente por definiciones de variables que son referenciadas desde otros ficheros fuentes. El siguiente programa C, nos da como resultado el mayor de tres va- lores dados.
  • 93. Este programa esta formado por dos ficheros independientes, deno- minados progOl.c y prog02.c. /************************* PROGOl.C ************************* Fichero juente I - junci6n principal **************************************************************/ #dejine a 12 #dejine b 25 #dejine c 3 / * Funci6n max. Toma dos valores, pI y p2, y una rejerencia p3 */ extern iut max(iut pI, iut p2, iut *p3); main ( ) / *junci6n principal */ ( iut w = a, x = b, y = c; iut Z = 0; max(w, x, &z); / * z igual al mayor de w y x */ max(z, y, &z); / * z igual al mayor de z (anterior) e y d printj(" nmayor = %d n': z); } / ************************* PROG02.C ************************* Fichero juente 2 - junci6n max **************************************************************/ / * Funci6n max. Toma dos valores, pI y p2, y una rejerencia p3 */ iut max(iut pI, iut p2, iut *p3) { if (pI < p2) *p3 = pI; else *p3 = p2; / * el contenido de la direcci6n p3 es modijicado */ / * con el valor de pI */ / * el contenido de la direcci6n p3 es modijicado */ / * con el valor de p2 */ Para compilar y enlazar los modulos progOl.c y prog02.c, utilizar la siguiente orden:
  • 94. Se denomina ambito de una variable (scope) a la parte de un programa donde dicha variable puede ser referenciada por su nombre. Una variable puede ser limitada a un bloque, a un fichero, a una funci6n, 0 a una decla- raci6n de una funci6n prototipo. Hay cuatro clases de ambito: local, global 0 fichero, funci6n y es- tructura. Cuando una variable se declara fuera de todo bloque en un programa, es accesible desde su punta de definici6n 0 declaraci6n hasta el final del fi- chero fuente. Esta variable recibe el calificativo de global. Una variable global existe y tiene valor desde el principio hasta el fi- nal de la ejecuci6n del programa. Todas las funciones tienen caracter glo- bal. Un elemento con caracter global puede no ser accesible desde todas las partes del programa. Si la declaraci6n de una variable se hace dentro de un bloque, el acce- so a dicha variable queda limitado a ese bloque y a los bloques contenidos dentro de este por debajo de su punta de declaraci6n. En este caso, la va- riable recibe el calificativo de local 0 automatica. Una variable local existe y tiene valor desde su punta de declaraci6n hasta el final del bloque donde esta definida. Cad a vez que el control se pasa al bloque para su ejecuci6n, las variables son nuevamente definidas; y cuando finaliza la ejecuci6n del mismo, las variables dejan de existir. Un elemento con caracter local es accesible solamente dentro del bloque al que pertenece. EI siguiente ejemplo muestra el ambito de las variables, dependiendo de si estan definidas en un bloque 0 fuera de todo bloque.
  • 95. En este ejemplo, al tratar de definir el ambito de una variable, distin- guimos cuatro niveles: Las variables definidas en este nivel, son accesibles desde el punta de definicion hasta el final del programa. / * Definici6n de var] como variable GLOBAL */ int var] = 50; / * Definici6n de var] y var2 como variables LOCALES en BLOQUE ] Y BLOQUE 2 */ printjt'%d %d n': var], var2); / * escribe 100 y 200 */ ( / * COMIENZO DEL BLOQUE 2 */ / * Redefinici6n de la variable LOCAL var] */ int var] = 0; printj("%d %d n': var], var2); / * escribe 0 y 200 */ l /* FINAL DEL BLOQUE 2 */ printf("%d n': var]); h escribe ]00 */ l /* FINAL DEL BLOQUE ] */ printf("%d n': var]); / * escribe 50 */ l /* FINAL DE main( ) Y DEL PROGRAMA */
  • 96. Las variables definidas en este nivel, solamente son accesibles des- de la propia funci6n main( ) y, por 10tanto, son accesibles en los bloques 1 y 2. Las variables definidas en este nivel, solamente son accesibles en el interior del bloque 1 y, por 10 tanto, en el bloque 2. Las variables definidas en este nivel, solamente son accesibles en el interior del bloque 2. En el ejemplo anterior se observa que una variable global y otra local pueden tener el mismo nombre, pero no guardan relaci6n una con otra, 10cual da lugar a que la variable global quede anulada en el ambito de accesibilidad de la local del mismo nombre. Como ejemplo observar 10que ocurre en el programa anterior con varl. Los panimetros formales declarados en la lista de parametros de la definici6n de una funci6n, son locales a la funci6n; y 10s parametros de- clarados en la lista de parametros de la declaraci6n de una funci6n proto- tipo, tienen un ambito restringido a la propia declaraci6n de la funci6n. El nombre de un campo 0 miembro de una estructura es local a la misma y puede ser utilizado solamente: despues del operador "." aplicado a una variable del tipo de esa estructura, 0 despues del operador "- >" aplicado a un puntero a esa estructura. En los capitulos sucesivos, al tratar las estructuras y punteros a estructuras, veremos esto con mas detalle. Por defecto, todas las variables llevan asociada una clase de almacenamiento que determina su ac.cesibilidad y existencia. Los conceptos de accesibili- dad y de existencia tanto para variables como para funciones, pueden alte- rarse por los calificadores:
  • 97. auto register static extern almacenamiento automatico almacenamiento en un registro almacenamiento estatico almacenamiento externo Los calificadores auto 0 register pueden ser utilizados solamente con variables locales; el calificador extern puede ser utilizado solamente con variables globales 0 funciones; y el calificador static puede ser utilizado con variables locales, globales 0 fund ones. En una variable dec1arada a nivel externo, esto es, fuera de toda definicion de fundon, se pueden utilizar los calificadores static 0 extern 0 bien omitir el calificador. A nivel externo no se pueden utilizar los calificadores auto o register. Una variable dec1arada a nivel externo es una definicion de la variable o una referencia a una variable definida en otra parte. Esto quiere decir que la dec1aracion de una variable externa inicializa la variable acero, por defecto, 0 a un valor especificado. Una variable definida a nivel externo, puede ser accesible antes de su definicion, 0 en otro fichero, utilizando el calificador extern. Esto quiere decir, que la utilizacion del calificador extern tiene senti- do cuando la variable ha side definida a nivel externo, una vez, y solamen- .te una, en cualquier parte del programa y queremos tener acceso a ella en otra parte donde no es visible. / * referencia a la variable var definida a continuaci6n */
  • 98. maine ) ! var+ +; printf("%d n'; var); funcion~ ( ); I funcion~( ) ! var+ +; printf("%d n'; var); funcion~( ); I / * rejerencia a la variable var dejinida en el jichero uno */ funcion~( ) ! var+ +; printf("%d n'; var); I 3. La declaracion extern en el primer fichera, permite acceder a la variable var, antes de su definicion. Sin la declaracion extern, la variable global var no seria accesible en la funcion maine ).
  • 99. 4. La declaraci6n extern en el segundo fichero, permite acceder a la variable var en este fichero. 5. Si la variable var no hubiera sido inicializada explfcitamente, C Ie asignaria automaticamente el valor O. Si se utiliza el calificador static en la declaraci6n de una variable a nivel externo, esta solamente es accesible dentro de su propio fichero fuen- te. Esto permite declarar otras variables static con el mismo nombre en otros ficheros correspondientes al mismo programa. En una variable declarada a nivel interno, esto es, dentro de un bloque, se pueden utilizar cualquiera de los cuatro calificadores, u omitir el califi- cador, en cuyo caso se considera la variable como auto (local 0 automatica). Una variable declarada como auto solamente es visible dentro del blo- que donde esta definida. Este tipo de variables no son inicializadas auto- maticamente, por 10 que hay que inicializarlas explfcitamente, cuando sea necesario. Una variable declarada a nivel interno como static, solamente es visi- ble dentro del bloque donde esta definida; pero, a diferencia de las auto- maticas, su existencia es permanente, en lugar de aparecer y desaparecer al iniciar y finalizar la ejecuci6n del bloque que la contiene. Una variable declarada static es inicializada solamente una vez, cuan- do comienza la ejecuci6n del programa. No es reinicializada cada vez que se ejecuta el bloque que la contiene. Si la variable no es inicializada expli- citamente, C la inicializa automaticamente a O. Una declaraci6n register indica al compilador que la variable sera al- macenada, si es posible, en un registro de la maquina, 10 que producini programas mas cortos y mas rapidos. EI numero de registros utilizables para este tipo de variables, depende de la maquina. Si no es posible almacenar una variable register en un registro, se la da el tratamiento de automatica. Este tipo de declaraci6n es valida para variables de tipo int y de tipo pun- tero, debido al tamafio del registro.
  • 100. Una variable declarada como register solamente es visible dentro del bloque donde esta definida. Este tipo de variables no son inicializadas auto- maticamente, por 10 que hay que inicializarlas explicitamente, si es necesario. Una variable declarada extern, referencia a una variable definida con el mismo nombre a nivel externo en cualquier parte del programa. La de- claracion extern a nivel interno es utilizada para hacer accesible una varia- ble externa, en una funcion 0 modulo en el cual no 10 es. main( ) [ / * se hace referenda a la variable varl */ extern int varl; / * var2 es accesible solamente dentro de main. Su valor inidal es O. */ static int var2; / * var3 es almacenada en un registro, si es posible */ register int var3 = 0; / * var4 es declarada auto, por defecto */ int var4. = 0; varl + = 2; / * se escribefi los valores 7, 0, 0, 0 */ printft(%d %d %d %d n': varl, var2, var3, var4); juncion---l( ); I juncion---l( ) [ / * se define la variable local varl */ int varl = 15; / * var2 es accesible solamente dentro de fundon---l */ static var2 = 5;
  • 101. var2 + = 5; / * se escriben los valores ]5, ]0 */ printf((%d %d n': var], var2); J En este ejemplo, la variable var] es definida a nivel externo. En la fun- cion main( ) se utiliza una declaracion extern, para hacer accesible dentro de esta, la variable var]. La variable var2 declarada static es inicializada, por defecto, a O. En la funcion denominadafuncion~ se define la variable local varl, anulando asi a la variable €xterna var]. La variable var2, declarada static, es inicializada a 5. Esta definicion no entra en conflicto con la variable var2 de la funcion main( ), ya que las variables static a nivel interno son visibles solamente dentro del bloque donde estan declaradas. A continuacion la variable var2 es incrementada en 5, de tal forma que sifuncion~ fuera Hamada otra vez, el valor inicial para esta variable seria de 10, ya que las variables intern as declaradas static, conservan sus valores de una ejecucion a otra del bloque. Una fundon declarada static es accesible solamente dentro del fichero fuente en el que esta definida. Una funcion declarada extern es accesible desde todos los ficheros fuen- tes que componen un programa. Para presentar los formatos de las sentencias, macros y funciones de C, se aplicaran las mismas reglas enunciadas al principio del capitulo 2. Cuando se trate de presentar la sintaxis correspondiente a una macro o a una funcion, se dara la siguiente informacion:
  • 102. 1. Fichero con extension .h (# include < fichero.h » que contiene las definiciones y/o declaraciones con respecto a esa funcion y afines. 2. Funcion prototipo para indicar el tipo del resultado y la lista de argumentos. funcion prototipo y definicion de cada argumento total = 0; area = 3.141592 * r * r; cuenta + = 1; La sentencia de asignacion es asimetrica. Esto quiere decir que la ex- presion de la derecha es evaluada, y el resultado es asignado a la variable especificada a la izquierda. De acuerdo con esta definicion, no serfa valida la sentencia: Si la variable es de tipo puntero, solamente se la puede asignar una direccion de memoria, la cual sera siempre distinta de O. Un valor 0 (se escribeNULL) sirve para indicar que esa variable puntero no apunta a un dato valido.
  • 103. iut a = 10, *P; p = &a; / * se asigna a p fa direcci6n de a */ Las operaciones de entrada y salida no forman parte del conjunto de sen- tencias de C, sino que pertenecen al conjunto de funciones de la libreria estandar de C. Por ello, todo fichero fuente que utilice funciones de entra- da/salida correspondientes a la libreria estandar de C, necesita de las fun- ciones prototipo correspondientes a estas, par 10 que deb era contener la linea: Las dobles comillas significan que el fiehero especificado, debe ser bus- cado en el directorio actual de trabajo y si no se encuentra, la busqueda debe continuar en el directorio estandar para los ficheros con extensi6n .h (directorio include). Si el fichero especificado, en lugar de escribirlo entre comillas, 10 es- cribimos entre angulos: la busqueda de dicho fichero se efectua solamente en el directorio estan- dar para los ficheros con extensi6n .h (directorio include). La funci6n printf( ) escribe con formato, una serie de caracteres, 0 un va- lor, en el fichero de salida estandar stdout. Esta funci6n devuelve un valor entero igual al numero de caracteres escritos.
  • 104. especifica como va a ser la salida. Esta formado por caracteres ordinarios, secuencias de escape y especificaciones de forma- to. El formato se lee de izquierda a derecha. Cada argumento debe tener su correspondiente especificaci6n y en el mismo or- den. Si hay mas argumentos que especificaciones de formato, los argumentos en exceso se ignoran. justifica el resultado a la izquierda, dentro del ancho especi- ficado. Por defecto la justificaci6n se hace a la derecha. antepone el signo + (mas) 0 - (menos) al valor de salida. Por defecto solo se pone signo - a los valores negativos. rellena con ceros no significativos hasta alcanzar el ancho minimo. antepone un blanco al valor de salida sies positivo. Si se uti- liza junto con + entonces se ignora. cuando se utiliza con la especificaci6n de formate 0, x, 0 X, antepone al valor de salida 0, Ox, 0 OX respectivamente. Cuando se utiliza con la especificaci6n de formate e, E, 0 f, fuerza a que el valor de salida contenga un punta decimal en todos los casos. Cuando se utiliza con la especificaci6n de formate g, 0 G, fuerza a que el valor de salida contenga un punta decimal en todos los casos y evita que los ceros arrastrados sean truncados.
  • 105. minimo numero de posiciones para la salida. Si el valor a escribir ocupa mas posiciones de las especificadas, el ancho es incrementado en 10 necesario. Si el ancho y/o la precision se especifican con el caracter *, el valor para estos campos se toma del siguiente argumento entero. int ancho = 15, precision =2; float valor= 12.345; printj((% *. 4'~ancho, precision, valor); (double) valor con signo de la forma: [-]dddd.dddd. El nu- mero de digitos antes del punta decimal depende de la mag- nitud del numero y de la cantidad de decimales de la preci- sion, la cual es 6 por defecto.
  • 106. (double) valor con signo, en formato foe (el que sea mas compacta para el valor y precisi6n dados). (double) igual que g, excepto que G introduce el exponente E en vez de e. (int) un solo caracter, correspondiente al byte menos signifi- cativo. (cadena de caracteres) escribir una cadena de caracteres has- ta el primer caracter nulo (' 0'). (puntero a un entero). En el entero es almacenado el numero de caracteres hasta ahora escritos en el buffer. (puntero a void). Escribe la direcci6n apuntada por el argu- mento. Si se especifica 070p0 %Np se escribe solamente el offset de la direcci6n y si se especifica %Fp 0 %Ip se escribe una direcci6n segmentada (xxxx:yyyy). En este ultimo caso se espera un puntero a un valor far por ello bajo el modelo small, utilizar con el argumento a escribir, la construcci6n cast: (tipo far *)arg. la precisi6n especifica el minima numero' de digitos que se tienen que escribir. Si es necesario se rellena con ceros a la izquierda. Si el valor excede de la precisi6n, no se trunca.
  • 107. e,E,f 1a precisi6n especifica e1numero de digitos que tienen que ser escritos despues del punto decimal. E1valor es redondeado. Por defecto es 6. g,G 1aprecisi6n especifica e1maximo numero de digitos signifi- cativos (6 por defecto) que se tienen que escribir. s 1aprecisi6n especifica e1maximo numero de caracteres a ser escritos. Los caracteres que excedan este numero, se ignoran. F y N son una amp1iaci6n de Microsoft C, por 10 que no pertenecen a1 C estandar. se utiliza como prefijo con 10stipos d, i, 0, x, y X, para es- pecificar que e1argumento es short int, 0 con u para especi- ficar un short unsigned into se utiliza como prefijo con 10stipos d, i, 0, x, y X, para es- pecificar que e1argumento es long int, 0 con u para especifi- car un long unsigned intoTambien se utiliza con 10stipos e, E, f, g, y G para especificar un double en 1ugar de un float. se uti1iza como prefijo con 10stipos e, E, f, g, y G, para es- pecificar long double. # include <stdio.h> # include <stdlib.h>
  • 108. main( ) [ char car; static char nombre[ J = C<Latemperatura ambiente"; int a, b, c; float x, y, z; car = 'C'; a = 20,' b = 350; c = 1991; x = 34.5; y = 1234; z = 1.248; system(C<c!s"); / * limpiar la pantalla */ printf(C< n%s es de ': nombre); printj(C<%d grados %c n': a, car); printj(C< n "); printj(C<a = %6d tb = %6d tc = %6d n': a, b, c); printf(C< nLos resultados son los siguientes: n "); printj(C< n%5s t t%5s t t%5s n': C<x': 'Y: c<z"); printj(C< n"); printj(C< n%8.21 t%8.21 t%8.21': x, y, z); printj(C< n%8.21 t%8.21 t%8.21 n': x+y, y/5, z*2); printj(C< n n "); z *= (x + y); print1t'Valor resultante: %.31 n': z); J 34.50 1268.50/ 1234.00 246.80 1.25 2.50
  • 109. main( ) ! int i = 10, a = 12345; printjt'%d %n n': a, &i); / * hasta ahora hay en el buffer 12345bb (b = blanco) */ printj("%d n': i); printj(" n%10s n%10s n': "abc': tiabcdef"); Crintjt' n%-10s n%-10s n': "abc': "abcdef"); 1 Resultado: 12345 7 abc abcdef abc abcdef La funci6n scanf( ) lee datos de la entrada estandar stdin, los interpreta de acuerdo con el formato indicado y los almacena en los argumentos es- pecificados. Cada argumento debe ser un puntero a una variable cuyo tipo debe corresponderse con el tipo especificado en el formato. Esta funci6n devuelve un entero correspondiente al numero de datos leidos y asignados de la entrada. Si este valor es cero, significa que no han sido asignados datos. Cuando se intenta leer un end-of-file (marca de fin de fichero) la funci6n scanf( ) retorna un EOp, constante definida en el fichero stdio.h.
  • 110. interpreta cada dato de entrada. Esta formado por caracteres en blanco «: t, n), caracteres ordinarios yespecificacio- nes de formato. El formato se lee de izquierda a derecha. Cada argumento debe tener su correspondiente especificaci6n de forma.to y en el mismo orden. Si un caracter en la entrada estandar no se corresponde con la entrada especificada por el formato, se interrumpe la en- trada de datos. argumento es un puntero a la variable que se quiere leer. ~ando se especifica mas de un argumento, los valores correspondientes en la entrada hay que separarlos por uno 0 mas espacios en blanco « : t, n) 0 por el caracter que se especifique en el formato. Un espacio en blanco antes 0 despues de una especificaci6n de forma- to hace que scanf( ) lea, pero no almacene, todos los caracteres espacio en blanco, hasta encontrar un caracter distinto de espacio en blanco. scanf((%d %j %c': &a, &b, &c); scanf((%d, %j, %c': &a, &b, &c); scanf((%d : %j: %c': &a, &b, &c); 5 23.4 b 5, 23.4 , b 5:23.4 : b Especificaciones de formato que no incluyan espacios en blanco como separadores, no son aconsejables por ser muy rigidas en su uso.
  • 111. scanf(H%d%f%c': &a, &b, &c); scanf(H%d,%f,%c': &a, &b, &c); 5 23.4b 5,23.4,b un aster isco a continuacion del simbolo 070 suprime la asigna- cion del siguiente dato en la entrada. maximo numero de caracteres a leer de la entrada. Los caracte- res en exceso no son tenidos en cuenta. F y N son una ampliacion de Microsoft C, por 10que no perte- necen al C estandar. h se utiliza como prefijo con 10stipos d, i, n, 0, y x, para especifi- car que el argumento es short int, 0 con u para especificar un short unsigned into se utiliza como prefijo con 10stipos d, i, n, 0, y x, para especifi- car que el argumento es long int, 0 con u para especificar un
  • 112. long unsigned intoTambien se utiliza con los tipos e~f, y g para especificar un double. tipo el tipo determina si el dato de entrada es interpretado como un canlcter, como una cadena de caracteres 0 como un numero. El formate mas simple contiene el simbolo 070 y el tipo. Por ejem- plo: %i. el argum. es car. un puntero a entrada esperada u unsigned int 0 int x~X int f e~E g~G float c char s char n int enteros con signo en base 10, 16 u 8. Si el entero co- mienza con 0 se toma el valor en octal y si empieza con Ox 0 OX el valor se toma en hexadecimal. en el entero es almacenado el numero de caracteres leidos del buffer 0 del fichero. Por ejemplo: long a; int r; scanf("%ld%n'~ &a, &r); printf("Caracteres lefdos: %ld n'~ r);
  • 113. el argum. es car. un puntero a entrada esperada p puntero a void lee una direcci6n y la almacena en el argumento. EI dato leido es interpretado como un valor en hexade- cimal. Por ejemplo: int *a; scanjt'%p': &a); main( ) [ int a, r; float b; char c, s[20J; printf("Introducir un valor entero, un real y un char n = > "); r = scanf("%d %j %c': &a, &b, &c); printf(" nNumero de datos lefdos: %d n': r); printf("Datos lefdos: %d %j %c n': a, b, c); printf(" n n"); printj("Valor hexadecimal: "); scanf("%i': &a); printf("Valor decimal: %i n': a); J lntraducir un valor entera, un real y un char = >12 3.5 x Numera de datos leidos: 3 Datos leidos: 12 3.500000 x Valor hexadecimal: OxAB Valor decimal: 171 Con la especificaci6n de formato OJoc, se lee cualquier carclcter, inclu- yendo los espacios en blanco (' " t, n).
  • 114. Por ejemplo, el siguiente ejercicio lee caracteres de la entrada estan- dary los escribe en la salida estandar. La entrada de datos finalizara cuan- do pulsemos Ctrl+Z (end-of-file). # include <stdio.h> main( ) [ char car; int r; r = scanf(C<%c': &car); while (r != EOF) [ printf(C<%c': car); r = scanf(C<%c': &car); ~ I l Sabemos que la funci6n scanf( ) lee datos delimitados por espacios en blanco. Pues bien, para leer cadenas de caracteres que contengan espa- cios en blanco, tenemos que sustituir la especificaci6n de formato OJospor O1o[A n], por ejemplo, que indica leer caracteres hasta encontrar un carac- ter n. scanf(C<%[A nr: nombre); printf("%s': nombre); Entrada: Francisco Javier Resultado: Francisco Javier Observar que nombre no lleva el operador de direcci6n &, por tratarse de un array. El identificador nombre es un puntero (direcci6n) a la cadena de caracteres. Si en lugar de especificar el formato %[A nJ se hubiera especificado el formato %s, el resultado hubiera sido: Francisco. Un conjunto de caracteres entre [ ], como formato, indica leer carac- teres hasta que se lea uno que no este especificado en el conjunto. El efecto
  • 115. inverso se consigue anteponiendo al conjunto de caracteres el simbolo esto es, [Acaracteres). Lee un canlcter de la entrada estandar stdin y avanza la posici6n de lectura al siguiente caracter a leer. Esta funci6n devuelve el caracter leido, 0 un EOF si se detecta el final del fichero 0 si ocurre un error. car = getchar( ); / * lee un cardcter y 10 almacena en la variable car */ Escribe un caracter en la salida estandar stdout en la posici6n actual y avanza a la siguiente posici6n de escritura. putchar(car); / * escribe el cardcter contenido en la variable car */
  • 116. main( ) { char car; printf("Introducir texto. Finalizar con AZ n"); while((car = getchar( )) != EOF) putchar(car); Cuando se estan introduciendo datos a traves del teclado y pulsamos la tecla Enter (en otros ordenadores New Line, Return) se introduce el carac- ter denominado fin de linea, cuya representaci6n en C se hace por medio de la secuencia de escape n. Igualmente, el caracter fin de fichero, representado simb6licamente por EOF, se obtiene bajo el sistema operativo UNIX pulsando las teclas Ctrl +D y bajo el sistema operativo DOS pulsando las teclas Ctrl + Z. EOF es una constante definida en el fichero stdio.h y tiene un valor de -1. El siguiente programa lee, repetidamente, datos de la entrada estan- dar hasta encontrar la marca de fin de fichero. main( ) { int a, r, char c;
  • 117. do { printf("a%d = '~ i); r = scanf("%d'~ &a); while ( r / = EOF) { if ( r ) i+ +; else jjlush(stdin); printjt'a%cr-=-----'~ 0; r = scanf("%d'~ &a); J printjt';,Desea jinalizar? sin: "); clearerr(stdin); c = getchar( ); J while ( c /= 's'); J Si introducimos un valor no valido para a[i], por ejemplo "a", no sera asig- nado (r= 0) permaneciendo en el buffer asociado con la entrada estandar, 10 que d~.;ugar a un bucle infinito; jjlush(stdin) borra el contenido del buffer asociado con la entrada estandar, 10 que permitira que scanj solicite un nuevo dato de la entrada. Cuando se teclea Ctrl +Z (0 Ctrl +D en UNIX) se activa el indicador de fin de fichero asociado con stdin (r=EOF), y finaliza la repetitiva while; Mien- tras este indicador no se desactive, cualquier funci6n que intente leer de la entrada estandar, se encontrara con una condici6n de fin de fichero y devol- vera un valor EOP. Por esta causa, en el ejemplo, getchar( ) no solicitara un dato de la entrada estandar, 10 que nos conduce de nuevo a un bucle infinito; clearerr(stdin) desactiva el indicador de fin de fichero de la entrada estandar, 10 que permitira que getchar() solicite un nuevo dato de la entrada.
  • 118. La funci6n getch( ) lee un canicter del teclado, sin visualizarlo; la funci6n getche( ) lee un canicter del teclado visualizandolo. ~ int getche(void); Ambas funciones leen un caracter de la memoria intermedia del tecla- do. Cuando se ejecuta una funci6n de estas, la ejecuci6n se detiene hasta que se pulse una tecla. No es necesario pulsar Enter. El resultado es un byte cuando la tecla pulsada se corresponde con uno de los caracteres de la tabla de C6digos de Caracteres de ASCII; y el resultado son dos bytes cuando la tecla 0 combinaci6n de teclas pulsadas se corresponden con al- guna de la tabla de los C6digos Extendidos; estas tablas se pueden ver en los apendices. Para este ultimo caso, hay que Hamar a la funci6n dos ve- ces, ya que es la segunda Hamada, la que proporciona el c6digo deseado (segundo c6digo). printf("pufse una tecla para continuar "); getche( ); En este ejemplo, la ejecuci6n continuara despues de pulsar una tecla, la cual sera visualizada. EI siguiente ejemplo, almacena en la variable byte2, el c6digo extendi- do de la tecla de funci6n, tecla de movimiento del cursor, combinaci6n de teclas etc., que se pulse. printj("pufse fa combinaci6n de teclas cuyo c6digo extendido desea conocer n "); byte] = getch( ); byte2 = getch( ); printf("%d t %d'~ byte], byte2);
  • 119. Fl Alt+A Shift+FlO Ctrl+Home flecha hacia arriba ~ 59 30 93 119 72 Esta funci6n pasa la cadena de caracteres al interprete de 6rdenes del siste- ma operativo, para ejecutar la orden indicada. La cadena de caracteres representa una orden para MS-DOS. Si la ca- dena de caracteres es nula, la funci6n simplemente comprueba si el inter- prete de 6rdenes COMMAND.COM esta presente. Si la cadena de caracte- res no es nula, la funci6n retorna un valor 0 si la orden es ejecutada, y un valor distinto de cero si ocurre un error, como por ejemplo: no se encuen- tra COMMAND.COM, la cadena excede de 128 bytes, 0 no hay suficiente espacio de memoria para ejecutar la orden. system("cls"); II limpiar fa pantalla r == system("dir *.c"); I Realizar un programa que de como resultado el interes producido ) el capital total acumulado de una cantidad c, invertida a un interes rOJo al ana.
  • 120. # include "stdio.h" # include "stdlib.h" main( ) ! double c, intereses, capital; float r; / * Entrada de datos */ printj("Capital invertido "); scanf("%/f': &c); printj(" nA un %% anual del "); scanf("%f': &r); printf(" n n n"); / * Cdlculos */ intereses = c * r / 100; cJpital = c + intereses; / * Escribir resultados */ printf("Intereses producidos %10.0lf n': intereses); printj("Capital acumulado %10.0lf n': capital); 1 Realizar un programa que de como resultado las soluciones reales xl y x2 de una ecuaci6n de segundo grado, de la forma: Las soluciones de una ecuaci6n de segundo grado vien en dad as por la expresi6n: -b ± sqrt(b2 - 4*a*c) 2*a
  • 121. # include "stdio.hn # i."1c1ude"stdlib.h n # include "math.h n main( ) ( ---double a, b, c, d, xl, x2; / * Entrada de datos */ printf("Introducir coeficientes a b c: n); scanf("%!j %!j %!j': &a, &b, &c); / * Cdlculo de las soluciones */ d = sqrt(b * b - 4 * a * c); xl (-b + d) / (2 * a); x2 = (-b - d) / (2 * a); / * Escribir resultados */ printf("EI valor de los coeficientes es: nn); printf("a = %g t b = %g t c = %g n': a, b ,c); printft' nSoluciones: nn); printf("xl = %g nx2 = %g n': xl, x2); ) Ellector podrei comprobar que para algunos valores de a, bye se ob- tiene un error. Eso es debido a que d toma un valor negativo, 10 que indica que no hay raices reales sino complejas. Para dar soluci6n a este proble- ma, ver las sentencias de control en el pr6ximo capitulo.
  • 122. Toma una decision referente a la accion a ejecutar en un programa, basan- dose en el resultado (verdadero 0 falso) de una expresion. if(expresion) sen ten cial; [else sentencia2] ; expresion debe ser una expresion numerica, relacional 0 logica. El resultado que se obtiene al evaluar la expresion es verda- dero (no cero) 0 falso (cero). sentencial12 representan una sentencia simple 0 compuesta. Cada sen- tencia simple debe estar separada de la anterior por un pun- to y coma. 1 Si el resultado de la expresion es verdadero, se ejecutani 10 indicado por la sentencial.
  • 123. Si el resultado de la expresion es falso, se ejecutani 10 indicado por la sentencia2. Si el resultado de la expresion es falso, y la clausula else se ha omitido, la sentencia1 se ignora. En cualquier caso, la ejecucion continua con la siguiente sentencia eje- cutable. if (x) b=a/x; b=b+1; En este ejemplo la expresion es una expresion numeric a x. Entonces b = a / x, que representa la sentencia1, se ejecutara si la expresion es ver- dadera (x distinta de 0) y no se ejecutara si la expresion es falsa (x igual a 0). En cualquier caso, se continua la ejecucion en la linea siguiente, b=b+J. En este otro ejemplo, la expresion a < b es una expresion de relacion. La sentencia c = c + 1, solo se ejecutara si a es menor que b. Si a es mayor o igual que b, se continua en la linea siguiente, ignorandose la sentencia c=c+J. if (a && b) x = i; En este ejemplo, la expresion a && b es una expresion logica. La sen- tencia x = i solo se ejecuta si a y b son distintos de cero. En otro casa, la sentencia x = i se ignora. if (a = = b * 5) { x = 4; a a+x; } else b = 0;
  • 124. En el ejemplo anterior, si se cumple la condici6n a = = b~, se ejecu- tan las s~ntencias x = 4 y a = a + x. En otro caso, se ejecuta la sentencia b = O. En ambos casos, la ejecuci6n continua en la siguiente linea de programa. if (car = = 's') break; La sentencia break-se-ejecutani solamente cuando car sea igual al ca- racter 's: Las sentencias if ...else pueden estar anidadas. Esto quiere decir que como sentencial 0 sentencia2, de acuerdo con el formato, puede escribirse otra sentencia if. if (expresionl) ! if (expresion2) sentencial; I else sentencia2; if (expresionl) if (expresion2) sentencial; else sentencia2;
  • 125. EI siguiente segmento de programa comprueba como es un numero a con respecto a otro b. if (a > b) printj(H%d es mayor que %d': a, b); else if (a < b) printj(H%d es menor que %d': a, b); else printj(H%d es igual a %d': a, b); Cuando en una linea de programa aparecen sentencias if ...else ani- dadas, Ia regIa para diferenciar cada una de estas sentencias, es que cada else se corresponde con el if mas proximo que no haya sido emparejado. if (a = = b) if (b = = e) printj(Ha = b = e"); else printj(Hb /= e"); En este ejemplo aparecen dos sentencias if anidadas. Aplicando Ia re- gIa anterior, el else se corresponde con el segundo if. if (a = = 0) if (b /= 0) s=s+b; else s=s+a;
  • 126. En este otro ejemplo, cuando a / = 0 se pasa a ejecutar la siguiente linea de programa. Si 10 que se desea es que se ejecute s = s + a cuando a / = 0, entonces tendriamos que escribir: if (a = = 0) [ if (b /= 0) s s+b; ---J else s=s+a; Realizar un programa que de como resultado el menor de tres n4me- ros a, b, c. # include <stdio.h> # include <stdlib.h> main( ) [ float a, b, c, men or; system("c!s"); printf(HNumeros a b c : "); scanf(H%f %f %1': &a, &b, &c); if (a < b) if (a < c) menor a; else menor =c; else if (b < c) menor b; else printf(Hmenor J
  • 127. La estructura presentada a continuaci6n, aparece con bastante frecuencia , y:es por 10 que Ie damos un tratamiento por separado. Esta estructura es , ,consecuencia de las sentencias if anidadas . .' . S:-':· ,:,: .. (i~t2;,:;,< if (expresionl) sentencial; else if (expresion2) sentencia2; else if (expresion3) sentencia3; else sentenciaN Si se cumpIe la expresionl, se ejecuta la sentencial y si no se cumple se examinan secuencialmente las expresiones siguientes hasta else, ejecu- tandose la sentencia correspondiente al primer else if, cuya expresi6n sea cierta. Si todas las expresiones son falsas, se ejecuta la sentenciaN corres- pondiente a else. En cualquier caso, se continua en la sentencia que sigue a la estructura. Al efectuar una compra en un cierto almacen, si adquirimos mas de 100 unidades de un mismo articulo, nos hacen un descuento de un 40 0/0, entre 25 y 100 un 20 070, entre 10 y 24 un 10 % y no hay descuento para una adquisici6n de menos de 10 unidades. Calcular el importe a pagar. # include <stdio.h> # include <stdlib.h>
  • 128. main( ) ( int ar, cc; float pu; systemt ecls"); printj("C6digo artlculo................. "); scanf("%d': &ar); printf(" nCantidad comprada....... "); scanf("%d': &cc); printf(" nPrecio unitario................. "); scanf("%f': &pu); printf(" n n%10s %1Os %10s %1Os %10s n n': "Artlculo': "Cantidad': "P. U:: "Dto:: "Total"); if (cc > 100) printj(" %9d%% %10.2/ n': 40, cc * pu * 0.6); else if (cc > = 25) printf(" %9d%% %10.2/ n': 20, cc * pu * 0.8); else if (cc > = 10) printf(" %9d%% %10.2/ n': 10, cc * pu * 0.9); else printf(" %1Os %10.2/ n': "--': cc * pu); Para poder imprimir un simbolo con un significado especial para C, este tiene que ser duplicado en la expresi6n correspondiente. Como ejem- plo, observar en el ejercicio anterior el formato %9d%%: %9d es el for- mato utilizado para escribir el tanto por ciento de descuento y %% es para escribir a continuaci6n el canicter "%': Esta sentencia permite ejecutar una de varias acciones, en funci6n del va- lor de una expresi6n.
  • 129. [switch (expr-test) { [declaraciones] case cte.l: [sentencial;] [case cte.2:] [senten cia2;] [case cte.3:] [sentencia3;] [default:] [sentenciaJv,·] cte.i e~una constante entera, una constante de un solo canicter 0 una expresion constante; en todos los casos, el valor resultante tiene que ser entero. Al principio del cuerpo de la sentencia switch, pueden aparecer decla- raciones. Las inicializaciones, si las hay, son ignoradas. La sentencia switch evalua la expresion entre parentesis y compara su valor con las constantes de cada case. La ejecucion de las sentencias del cuerpo de la sentencia switch, comienza en el case cuya constante coincida con el valor de la expr-test y continua hasta el final del cuerpo 0 hasta una sentencia que transfiera el control fuera del cuerpo (por ejemplo break). La sentencia switch puede incluir cualquier numero de c1<iusulascase. Si no existe un valor igual al valor de la expr-test, entonces se ejecutan las sentencias a continuacion de default, si esta c1<iusulaha side especifica- da. La chiusula default puede colocarse en cualquier parte del cuerpo y no necesariamente al final.
  • 130. Leer una fecha representada por dos enteros, mes y ano y dar como resuitado Ios rlias correspondientes al meso Tener en cuenta que Febrero puede tener 28 0 29 dias si el ano es bisiesto. Un ano es bisiesto cuando es muIti- pIa de 4 y no de 100 0 cuando es muitiplo de 400. # include <stdia.h> # include <stdlib.h > main( ) [ unsigned iot dd, mm, aa; system(Hcls"); printft'Inlraducir mes (# #) y ana (# # # #): "); scanf(H%d %d': &mm, &aa); switch (mm) [ case 1: case 3: case 5: case 7: case 8: case 10: case 12: dd = 31; break; case 4: case 6: case 9: case 11: dd = 30; break; case 2: if ((aa 0/'0 4 0) && (aa % 100 != 0) II (aa % 400 0)) dd = 29; else dd = 28;
  • 131. break; default: printj(" nEI mes no es wi/ido n "); } if (mm > = 1 && mm < = 12) printf(" nEI mes %2d del ano %4d tiene %2d dfas n':mm,aa,dd); Esta sentencia finaliza la ejecuci6n de una sentencia do, for, switch, 0 while en la cual aparece. Cuando estas sentencias estan anidadas, la sentencia break solamente finaliza la ejecuci6n de la sentencia donde esta incluida. EI siguiente ejemplo calcula el importe a pagar por un vehiculo al cir- cular por una autopista. Se utiliza un tipo enumerado. Las variables de un tipo enumerado son tratadas como si fueran de tipo into A cada elemento de un tipo ordinal se Ie asigna el numero de orden partiendo del O.Este numero de orden puede ser alterado, como se hace en el ejemplo. # include <stdio.h> # include <stdio.h> main( ) { enum tipo_vehiculo { bidcleta = 1, moto, coche, camion
  • 132. enum tipo_vehiculo vehiculo; int km, tm, importe; system("c!s"); printf(" t1 - bic!eta n"); printf(" t2 - mota n"); printf(" t3 - coche n "); printf(" t4 - camion n"); printf(" n tPulse la opci6n deseada "); scanf("%d': &vehiculo); switch (vehiculo) { case bicic!eta: importe = 100; break; case mota: case coche: printj(" n;,Kil6metros? "); scanf("%d': &km); importe = 30 * km; break; case camion: printf(" n;,Kil6metros y toneladas? "); scanf("%d O/Od':&km, &tm); importe = 30 * km + 25 * tm; break; default: printf(" nLa opci6n no es correcta n "); exit(l); / * error; saIii'del program a */ I printj(" nlmporte I
  • 133. Ejecuta una sentencia, simple 0 compuesta, cero 0 mas veces, dependien- do del valor de una expresi6n. while (expresi6n) sentencia; 2. Si el resultado de la expresi6n es cero (falso), la sentencia no se ejecuta y se pasa a ejecutar la siguiente sentencia en el programa. 3. Si el resultado de la expresi6n es distinto de cero (verdadero), se ejecuta la sentencia y el proceso se repite comenzando en el punto 1. La rutina siguiente solicita obligatoriamente una de las dos respuestas posibles: sin (S1 0 no). printj(H nDesea continuar sin (si 0 no) "); while ((car = getche( )) /= 's' && car /= 'n') printj(H nDesea continuar sin (si 0 no) "); EI siguiente programa da como resultado la suma de una serie de can- tidades introducidas por teclado. La entrada de datos finaliza cuando pu]· semos hZ.
  • 134. # include <stdio.h > # include <stdlib.h> main( ) [ double sum 0, v; system("cls"); 1* borrar pantalla */ printj ("Pulse AZ (F6) para jinalizar la entrada n n"); printj(" nCantidad > > "); while (scanj("%lf': &v) /= EOF) [ printj ("%35.2j n': v); sum + = v; printj(" nCantidad > > "); l printj (" t tTOTAL%14.2j n': sum); while (1) sentencia; main( ) [ while (1) [ char car; printj(" nlntroduce un cardcter: "); car = getche( ); printj(" nEI c6digo ASCII de %c es %d n': car, car); l
  • 135. Realizar un programa que imprima 10snumeros z, comprendidos en- tre 1 y 50, que cumplan la expresi6n: / * Cuadrados que se pueden expresar * como sum a de otros dos cuadrados */ # include <stdio.h> # include <std/ib.h > # include <math.h > main( ) ( unsigned int x, y, z; system (Hcls"); printj("%lOs %lOs %lOs n': HZ': "X': "Y"); printf(" n n"); x=l;y=l; while (x < = 50) ( / * cafcufar fa parte entera (z) de fa raiz cuadrada */ z = sqrt(x * x + Y * y); while (y < = 50 && z < = 50) { / * comprobar si z es suma de dos cuadrados perfectos */ if (z * z = = x * x + Y * y) printj("%lOd %lOd %lOd n': z, x: y); y=y+l; z = sqrt(x * x + y * y); l x=x+l;y=x;
  • 136. Ejecuta una sentencia, simple 0 compuesta, una 0 mas veces, dependiendo del valor de una expresi6n. do sentencia; while (expresion); 3. Si el resultado de la expresion es cero (falso), se pasa a ejecutar la siguiente sentencia en el programa. 4. Si el resultado de la expresion es distinto de cero (verdadero), el proceso se repite comenzando en el punta 1. Calcular la raiz cuadrada de un numero n, por el metoda de Newton que dice: # include <stdio.h > # include <std/ib.h>
  • 137. main( ) ( double n; double aprox; double antaprox; double epsilon; 1* mimero *1 1* aproximacion a fa raiz cuadrada *1 I* anterior aproximacion a fa raiz cuadrada */ 1* coeficiente de error *1 system (Hcls"); printj(HNlimero: "); scanf(H%lf': &n); printj(HRaiz cuadrada aproximada:"); scanf(H%lf': &aprox); printjt'Coejiciente de error: "); scanf(H%lj': &epsilon); do { antaprox = aprox; aprox = (nlantaprox + antaprox) I 2; } while (fabs(aprox - antaprox) > = epsilon); printj(H n nLa raiz cuadrada de %.2lj es %.2lj n': n, aprox); } Numero: 10 Raiz cuadrada aproximada: 1 Coeficiente de error: le-6
  • 138. Cuando se desea ejecutar una sentencia simple 0 compuesta, repetidamen- te un numero de veces conocido, la construcci6n adecuada es la sentenciafor. for ([vI = eI, [v2 = e2]...];[condicion];[progresion-condj) sentencia; vi=ei vi representa una variable que sera inicializada con el valor de la expresi6n ei. condici6n es una expresi6n de Boole (operandos unidos por ope- radores relacionales y/o 16gicos). Si se omite, se supo- ne siempre que es verdadera. progresi6n-cond es una expresi6n cuyo valor evoluciona en el senti do de que se de la condici6n para finalizar la ejecuci6n de la senten cia for. 2.1 Si el resultado es distinto de cero (verdadero), se ejecuta la sentencia, se evalua la expresi6n que da lugar a la progresion de la condicion y se vuelve al punta 2. 2.2 Si el resultado de 2 es cero (falso), la ejecuci6n de la senten- cia for se da por finalizada y se continua en la siguiente sen- tencia del programa. for (i = 1; i < = 100; i + +) printf("%d ': i);
  • 139. Este ejemplo imprime los numeros dell aliOO. Literalmente dice: desde i igual a 1, mientras i sea menor 0 igual que 100, con incrementos de 1, escribir el valor de i. for (k = 7; k < = 112; k + = 7) printj(H%d ': k); float i; for (i = 1; i < = 10; i + = 0.5) printjt'%g ': i); for (a = 9; a > = 1; a--) printj(H%d ': a); for (,. ,) { Este ejemplo indica c6mo realizar un bucle infinito. La terminaci6n se realizani con Break 0 con Ctrl +C. En generallas sentencias repetitivas while, do, y for se pueden colocar in- distintamente unas dentro de otras para formar bucles anidados. Un bucle puede colocarse dentro de otro bucle y entonces se dice que estan anidados. En este caso el bucle interno se ejecutara total mente, cada vez que se ejecute el bucle que 10 contiene.
  • 140. Escribir un programa que imprima un triangulo construido con ca- racteres consecutivos del c6digo ASCII, como el que se muestra a conti- nuaci6n. # 0J0 & ( ) # include <stdio.h> # include <stdlib.h> main( ) ! char car; unsigned int ji/as, columnas; unsigned int njilas; printj("Ntimero de ji/as del tridngulo "); scanj("%d': &nji/as); for (fi/as = J, car = ' x20'; ji/as < = nji/as; ji/as+ +) I for (columnas J; columnas < = ji/as; columnas+ +) ( car+ +; printf("%5c': car); J printf(" n "); J 1
  • 141. Imprimir un tablero de ajedrez y sobre eI marcar con * las celdas a las que se puede mover un alfil desde una posicion dada. 2. Partiendo de la fila 1, columna 1y recorriendo por filas el tablero imprimir un(a): * si se cumple, que la suma 0 diferencia de la fila y columna ac- tuales, coincide con la suma 0 diferencia de la fila y columna donde se coloca el alfil. main( ) { lot falfi/, calfi/,· iot fila, columna; / *posicion del alfil */ / *posicion actual */ printf(t<Posicion del alfil (fila, columna): "); scanf(t<%d O/Od':&falfil, &calfil); for (fila = 1;fila < = 8; fi/a+ +) { for (columna = 1; columna < = 8; columna + +) { if ((fila + columna = = falfil + calfil) II (fila - columna = = falfil - calfil))
  • 142. printj(H * "); else if ({fila + columna) % 2 0) printj(HB "); else printj(HN "); l printf(H n"); / * cambiar de fila */ l l Esta sentencia, estando dentro de una sentencia do, while, 0 for, pasa el control para que se ejecute la siguiente iteraci6n. El siguiente programa imprime todos los numeros entre 1 y 100 que no sean multiplos de 5. main( ) ! iot n; for (n = 0; n < = 100; n + +) ! if (n % 5 = = 0) continue; printf(H%d ': n); l l Notar que cada vez que se ejecuta la sentencia continue, el cuerpo del for se abandona, iniciandose la ejecuci6n del mismo para un nuevo valor de n.
  • 143. La sentencia goto transfiere el control a una linea especifica del programa, identificada por una etiqueta. Si la linea a la que se transfiere el control es una sentencia ejecutable, se ejecuta esa sentencia y las que Ie siguen. Si no es ejecutable, la ejecuci6n se inicia en la primera sentencia ejecutable que se encuentre a continua- ci6n de dicha linea. No se puede transferir el control fuera de la funci6n en la que nos en- contramos. # include <stdio.h> # include <stdlib.h> main( ) ( float r, a; .system(Hcls"); printjt'Escriba un cero para jinalizar n"); Comienzo: printf(H nRadio: "); scanf(H%f': &r); if (r > 0) ( a = 3.141592 * r * r; printf(HArea = %.2j n': a); }
  • 144. else goto fin; goto Comienzo; fin: ; J Un uso abusivo de esta sentencia da lugar a programas dificiles de in- terpretar y de mantener. Por ello, en programaci6n estructurada, se utiliza solamente en ocasiones excepcionales. La funci6n que desempefia una sen- tencia goto, puede suplirse utilizando las sentencias de control estructura- das (if...else, do, for, switch, while). El uso mas normal consiste en abandonar la ejecuci6n de alguna es- tructura profundamente anidada, cosa que no puede hacerse mediante la sentencia break, ya que esta se limita unicamente a un solo nivel de anida- miento. Cuando e1 valor de alguno de los elementos de la matriz m sea igual a-I, salir. main( ) [ int f, c, m[8][8]; for if = 0; f < = 7; f + +) for (c = 0; c < = 7; c+ +) if (mUl[c] = = -1) goto salir; salir: if if < 8 && c < 8) printft'(%d,%d) n': J, c);
  • 145. 1. Si a = 0 y b = 0, imprimiremos un mensaje diciendo que la ecua- cion es degenerada. Indicar con literales apropiados, los datos a introducir, as! como los resultados obtenidos. # include <stdio.h> # include <stdlib.h> # include <math.h > main( ) { double a, b, c; double d; / * coeficientes de fa ecuacion */ / * discriminante */
  • 146. system("c/s"); / * borrar la pantalia */ printf("Coejicientes a, bye de la ecuaci6n: "); scanjt'%lj %lj %lj': &a, &b, &c); printf(" n n "); if (a = = 0 && b = = 0) printj("La ecuaci6n es degenerada n "); else if (a = = 0) printj("La unica rafz es: %.2lj n': -c / b); else [ re = -b / (2 * a); d=b*b-4*a*c; im = sqrt(fabs(d)) / (2 * a); if (d > = 0) [ printf("Rafces reales: n"); printj("%.2lj %.2lj n': re + im, re - im); ) else [ printf("Rafces complejas: n "); printj("%.2lj + %.2lj i n': re, jabs(im)); printj("%.2lj - %.2lj i n': re, jabs(im)); ) ) Escribir un programa para que lea un texto y de como resultado el numero de palabras con al menos cuatro vocales diferentes. Suponemos que una palabra esta separada de otra por uno 0 mas espacios (' '), carac- teres tab ( t) 0 caracteres nueva linea ( n).
  • 147. / ********** Palabras con cuatro 0 mas vocales diferentes **********/ # include <stdio.h > # include <stdlib.h > main( ) ( int np = 0; / * numero de palabras con 4 vocales distintas */ int a = 0, e = 0, i = 0, 0 = 0, u = 0; char car; printjt7ntroducir texto. Finalizar la entrada con AZ n"); while ((car = getchar( )) /= EOF) ( switch (car) { case ~': case 'a': a = 1; break; case 'E': case 'e': e = 1; break; case 'f': case 'i': i = 1; break; case '0': case '0': o = 1; break; case 'V': case 'u': u = 1; break; default: if (car == " II car == 't' II car == 'n') { if ((a + e + i + 0 + u) > = 4) np + = 1; . a=e=i=o=u=O; I I/*fin del switch */ } / *fin del while */ if ((a + e + i + 0 + u) > = 4) np + = 1; printj(" nNumero de palabras con 4 vocales distintas: %d': np); }
  • 148. Escribir un programa para que lea un texto y de como resultado el numero de caracteres, el numero de palabras y el numero de lineas del mis- mo. Suponemos que una palabra esta separada de -otra por uno 0 mas es- pacios (' '), caracteres tab ( t) 0 caracteres nueva linea ( n). # include <stdio.h> # include <stdlib.h> const int Sf = 1; const int NO = 0; main( ) / *funcian principal */ [ char car; int palabra = NO; int ncaracteres = 0, npalabras = 0, nlineas = 0; printj("fntroducir texto. Finalizar cada /(nea con CR. n "); printjt'Finalizar la entrada con AZ. n n"); while ((car = getchar( )) 1= EOP) [ + +ncaracteres; if (car = = " II car palabra = NO; else if (palabra = = NO) [ / * contador de caracteres */ = = ' n' II car = = ' t') / * eliminar blancos, tabuladores y */ / *finales de linea entre palabras */ / * comienza una palabra */ + + npalabras; palabra = Sf; if (car = = ' n') + +nlineas; / *finaliza una linea */ / * contador de lineas */ ) printj("%d %d %d n': ncaracteres, npalabras, nlineas); )
  • 149. Realizar un programa que a traves de un menu permita, realizar las operaciones de sumar, restar, multiplicar, dividir y salir. Las operaciones constanin solamente de dos operandos. # include <stdio.h > # include <stdio.h > main( ) ( double dato1, dato2, resultado; int operacion; double sumar(double dato1, double dato2); double restar(double dato1, double dato2); double multiplicar(double dato1, double dato2); double dividir(double dato1, double dato2); void menu(void); while (1) { do ( system(Hcls"); menu( ); scanj(H%d': &operacion); J while (operacion < 1 II operacion > 5); if (operacion 1= 5) ( printf(H nTeclear dato 1: "); scanft'%lf: &dato1); printf(H nTeclear dato 2: "); scanj(H%lf: &dato2);
  • 150. switch (operacion) ( case 1: resultado = sumar(dato1, dato2); break; case 2: resultado restar(dato1, dato2); break; case 3: resultado multiplicar(dato1, dato2); break; case 4: resultado = dividir(dato1, dato2); break; J printj(H n nResultado = %g n': resultado); printj(H nPufse una tecla para continuar "); getch( ); J else break; void menu( ) ( printj(H n tl. sumar n "); printj(H n t2. restar n "); printj(H n t3. multiplicar n"); printj(H n t4. dividir n "); printf(H n t5. salir n"); printj(H n nSefeccione fa operaci6n deseada: "); J double sumar(double a, double b) ( double c; c=a+b; return(c); )
  • 151. double restar(double a, double b) [ double c; c = a - b; return (c); J double multip/icar(double a, double b) [ double c; c = a * b; return(c); J double dividir(double a, double b) [ double c; c=a/b; return(c); J Un algoritmo que genere una secuencia aleatoria 0 aparentemente aleato- ria de numeros, se llama un generador de numeros aleatorios. Muchos ejem- plos requieren de este metodo. EI metoda mas comunmente utilizado para generar numeros aleato- rios es el metoda de congruencia lineal. elida numero en la secuencia rk, es calculado a partir de su predecesor rk-l' utilizando lasiguiente formula: La secuencia as! generada, es Hamada mas cprrectamente secuencia pseudoaleatoria, ya que cada numero generado, depende del anterior men- te generado.
  • 152. to- m- ncia en- El siguiente algoritmo, presentado como una fund on C, genera 65536 numeros aleatorios y no causara sobrepasamiento en un ordenador que ad- mita un rango de enteros de _231 a 231 - 1. void rnd(long *prandom) [ La Hamada a esta fundon, pasa el parametro por referenda, con la finalidad de generar un numero random diferente cada vez. La fundon ge- nera numeros enteros comprendidos entre 0 y 65535. Para la mayoria de las aplicaciones, estos numeros deberian estar com- prendidos dentro de un intervalo requerido. Por ejemplo, si el problema simula la tirada de un dado, podriamos escribir: # include <stdio.h > # include <stdlib.h> # include <ctype.h > main( ) [ unsigned int inicio,o long random = inicio,o / * random int tirada,o char c,o system(Hcls"),o printf(HPara tirar el dado, pulse una tecla n "),o printf(HPara 1inalizar pulse <1>. n n"),o c = getch( ),o / * tolower convierte a mimisculas el contenido de c */ while (tolower(c) /= '!') [ rnd(&random),o tirada = random % 6 + 1;
  • 153. printj("%10d%c': tirada, ( r'); c = getch( ); void rnd(long *prandom) ( Frecuentemente requerimos de un valor aleatorio entre 0 y 1. Para este proposito podemos utilizar una version modificada como la que se expone a continuacion: main( ) { unsigned int inicio; long random = inicio; / * random double n; int i; for (i = 10; i; i--) ( n = rnd(&random); printj("%.8g n': n); 1 } double rnd(long *prandom) { *prandom = (25173 * *prandom + 13849) % 65536; return((double) *prandom / (double)65535); }
  • 154. Supongamos que tenemos un solido irregular S, el cual puede encerrarse en un cubo C. Puede demostrarse que la probabilidad de que un punto al azar dentro de C, este tambien dentro de S es: Un octavo de la esfera, as! definida, esta dentro del cubo de lado 1. Por 10que si generamos un punto a1 azar, 1a probabi1idad de que este se encuentre tambien dentro del sector esferico es: Por 10tanto, para saber e1vo1umen de 1aesfera, basta calcu1ar esa pro- babilidad. # include < stdio.h > # include <stdlib.h> coost iot TOTAL = 1000; main( ) [ float vofumen; / * vofumen de fa esjera */ iot dentro,o / * ntimero de puntos dentro de fa esjera */ iot DentroEsjera(coost iot);
  • 155. systemt 'cis"); printj("Ensayos a realizar: %d n n': TOTAL); dentro = DentroEsjera(TOTAL); / * Es necesario poner 8.0 para que el resultado sea real */ volumen = 8.0 * dentro / TOTAL; printj(" n n nVolumen estimado = %g n': volumen); l / * Puntos generados dentro de la esjera */ int DentroEsjera(const int total) [ unsigned int inicio; long random = inicio; int i, dentro = 0; double x, y, z; for (i = 1; i < = total; i+ +) [ printf("Realizando cdlculos... %d%c': i, ' r'); x = rnd(&random); y = rnd(&random); z = rnd(&random); if (x*x + y*y + z*z < = 1) dentro = dentro + 1; l return( dentro); l /* Generador de numeros pseudoaleatorios */ double rnd(long *prandom) [ *prandom = (25173 * *prandom + 13849) % 65536; return((double) *prandom / (double)65535); l
  • 156. TIPOS ESTRUCTURADOS DE DATOS Un array es una estructura homogenea, compuesta por varias componen- tes, todas del mismo tipo y almacenadas consecutivamente en memoria. Cada componente puede ser accedido directamente por el nombre de la variablearray seguido de uno 0 mas subindices encerrados entre corchetes. La representacion de los arrays se hace mediante variables suscritas o·de subindices y pueden tener una 0 varias dimensiones (subindices). A los arrays de una dimension se les llama tambien listas; y a los de dos di- mensiones, tablas. Desde el punta de vista matematico, en mas de una ocasion necesita- remos representar variables, tales como: all al2 al3 a21 a22 a23
  • 157. si se utilizan dos subindices. Para realizar esta misma representaci6n bajo C, tendremos que recurrir a los arrays que acabamos de definir y que a continuaci6n se estudian. Por ejemplo, supongamos que tenemos un array unidimensionailla- made datos, el cual contiene tres elementos. Estos elementos se identifica- rein de la siguiente forma: N6tese que los subindices son enteros consecutivos, y que el primer subindice vale O. Un subindice puede ser cualquier expresi6n entera. Un array de dos dimensiones se representa mediante una variable con dos subindices (filas, columnas); un array de tres dimensiones se represen- ta mediante una variable con tres subindices etc. El numero maximo de di- mensiones 0 el numero maximo de elementos para un array depende de la memoria disponible. La dec1araci6n de un array especifica el nombre del array, e1numero de elementos del mismo y el tipo de estos. tipo nombre [tamafio}; tipo nombre [ ]; tipo indica el tipo de 10s elementos del array. Puede ser cualquier tipo excepto void.
  • 158. tamaiio es una constante que especifica el numero de elementos del array. EI tamafio puede omitirse cuando se inicializa el array, cuando se declara como un panimetro formal en una funcion 0 cuando se hace referencia a un array declarado en otra parte del programa. Este ejemplo declara una variable array denominada lista con 100ele- mentos (del 0 al 99), cada uno de ellos de tipo into Este ejemplo declara una variable array denominada nombre con 40 elementos (0 a 39), cada uno de ellos de tipo char. Este ejemplo declara el tipo y el nombre de un array de punteros a objetos de tipo char. La definicion actual de vector se hace en otra parte del programa. Un elemento de un array se puede utilizar exactamente igual que una variable. Por ejemplo, las siguientes operaciones son validas: int lista[100], k, a; a = lista[1] + lista[99J' k = 50; lista[k] + = 1; Notar que para referenciar un elemento de un array se puede emplear comosubindice una constante, una variable 0 una expresion de tipo entero. tipo nombre [expr-cte][expr-cte] ...; tipo nombre [ ][expr-cte] ...;
  • 159. La primera expr-cte puede omitirse cuando se inicializa el array, cuando se declara como un panimetro formal en una funcion 0 cuando se hace referencia a un array declarado en otra parte del programa. int a[2j[3j[4j[5j[3j; char b[12j[5j; int c[ j[3j = [ 10, 12, 14, 16, 18, 20 }; Este ejemplo declara un array a de cinco dimensiones. EI numero de elementos es 2x3x4x5x3, todos de tipo into EI primer elemento es a[Oj[Oj[Oj[Oj[Ojy el ultimo a[1j[2j[3j[4j[2]. Tambh~n declara un array b de dos dimensiones, con 12x5 elementos de tipo char (b[Oj[Oja b[1lj[4j), e ini· cializa el array c de dos filas y tres columnas. EI lenguaje C no chequea los limites de un array. Es responsabilidad del programador el realizar este tipo de operaciones. Para dimensionar un array se pueden emplear constantes 0 expresio· nes a base de constantes de cualquier tipo entero. Para acceder a un elemento de un array, se hace mediante el nombre del array seguido de uno 0 mas subindices, dependiendo de las dimensio· nes del mismo, cada uno de ellos encerrado entre corchetes. Un subindice puede ser una constante, una variable 0 una expresion cualquiera. Algunos compiladores de C, no permiten inicializar arrays de una 0 mas dimensiones si estan declarados como locales (auto). En este caso, para inicializar un array, debemos definirlo como global; es decir, debe ser una variable que exista durante toda la ejecucion del programa. Esto se consi- gue definiendo el array a nivel externo 0 declarandolo como static.
  • 160. #include < stdio.h > #dejine N 10 main( ) ( static float x[ J = {1O,15,20,25,30,35,40,45,50,55}; float y[N][NJ, z[2 *NJ; / * declaraci6n de los arrays y, Z */ int f, c; j=l;c=9; yUJ[cJ = 20; z[c+ 10J = 30; / * rejerenciando elementos */ printft'%g %g %g n': x[cJ, YU][cJ, z[c+1O]); } Este ejemplo declara e inicializa un array x de una dimensi6n de tipo real. Tambien declara un array y de dos dimensiones y un array Z de una dimensi6n ambos de tipo real. A continuaci6n asigna el valor 20 al ele- mento y[1][9J y el valor 30 al elemento z[19J. La declaraci6n de un array como local (auto) puede dar lugar a que el tamafio reservado para la pila sea sobrepasado. Esto se debe a que la asignaci6n de memoria para las variables locales se hace a traves de la pila. Cuando esto ocurra, seremos informados mediante un mensaje de error: stack overflow. La soluci6n a este problema es aumentar el tamafio de la pila (stack) por medio de la opci6n correspondiente del compilador. Esta orden compila y enlaza el programa prog.c, utilizando una pila de tamafio 8K (2000H bytes). Los arrays son almacenados por filas. Por ejemplo, si inicializamos un array de 2 filas y 3 columnas de la forma:
  • 161. ( 168 ENCICLOPEDIA DEL LENGUAJE C Realizar un programa que asigne datos a una matriz unidimensional a de n elementos y, a continuaci6n, escribir el contenido de dicha matriz. main( ) { int a[N--.ELEMENTOS], n = 0, i; / * n = nOde elementos Iddos */ printj(Hlntroducir valores para la matriz. n"); printjteLa entrada jinalizard cuando se hayan introducido n"); printf(Hel total de los elementos 0 cuando se introduzca n"); printf(Hun valor no numerico. n n"); printf(Ha[%d]= ': n); while (n < N--.ELEMENTOS && scanf(H%d': &a[nJ)) { n+ +; printj(Ha[%d]= ': n); } / * Salida de datos */ printf(H n n ");
  • 162. for (i = 0; i < n,oi+ +) printf("%d ': a[iJ),o printf(" n nFin del proceso. n "),o J Realizar un programa que lea las notas correspondientesa los alum- nos de un determinado curso, las almacene en un array y de como resulta- do la nota media correspondiente al curso. # include <stdio.h> # include <stdlib.h> main( ) [ float notas[ALUM~AX], suma iut i, nalumnos,o system(' ecls"),o printf("Nzimero de alumnos: "),o scanf("%d': &nalumnos),o / * Entrada de datos */ for (i = 1; i < = nalumnos,o i+ +) [ printf("Alumno nzimero %3d, nota final: ': i); scanf("%f': &notas[i-1]),o suma + = notas[i-1],o / * Escribir resultados */ printj(" n nNota media del curso: %5.2f n': suma / nalumnos),o J
  • 163. Realizar un programa que asigne datos a una matriz t de dos dimen- siones y, a continuaci6n, escriba las sumas correspondientes alas filas de la matriz. #include <stdio.h > # include <stdlib.h > # define FILAS---.MAX 10 #define COLS---.MAX 10 / * mimero maximo de filas */ / * mimero maximo de columnas */ main( ) { float tfFILAS---.MAXj[COLS---.MAXj, sumafila,o int filas, cols, f, c,o printj("Numero de filas de la matriz: "),o scanf("%d': &filas); printj("Numero de columnas de la matriz: "),o scanj("%d': &cols),o / * Entrada de datos */ for if = 0;f < filas,of+ +) { printjt' nDatos para la fila %d n': f); for (c = 0; c < cols,oc++) scanj("%f': &tUj[c}),o / * Escribir la suma de cada fila */ printj(" n n"),o for if = 0;f < filas; f+ +) { sumafila = 0;
  • 164. for (c = 0; c < cols; c++) sumafila + = t[f][c]; printf(HFila %d, suma = %g n': j, sumafila); ] 1 Rea1izar un programa que a1macene en una matriz 10s tantos por ciento de aprobados correspondientes a 10s cursos de BUP en 10s alios 1986 a 1990 y, a continuaci6n, permita consu1tar dicha matriz. / * Tanto por ciento de aprobados en los cursos 1~ 2 a y 3 a * de BUP en los afios 1986 a 1990. */ #include <stdio.h> #include <stdlib.h> #define AC 1986 #define AF 1990 #define C (AF - AC + 1) main( ) { iut curso, anno; char x; / * Asignar valores a la matriz */ static float est! ][C] = { 70,68,73,69,71, 80,83,81,85,84, 79,83,81,85,82 ]; / * Presentar un dato cua1quiera */ do { system(Hcls");
  • 165. do [ printj("Curso que desea consultar (1, 2 0 3) "); scanj("%d': &curso); 1 while (curso < 1 II curso > 3); printjt' n "); do [ printj("Ano (%d a %d) ': AC, AF); scanj("%d': &anno); 1 while (anno < AC II anno > AF); printj(" n"); printj("Curso %dO de BUP. Ano %d; ': curso, anno); printj("lo superaron el %.1j %%': est[curso-1][anno-AC}); printjt' n n n"); printj("Mds consultas sin: "); do x = getche( ); while (x /= 's' && x /= 'n'); 1 while (x / = 'n'); 1 Realizar un programa para que lea una lista de valores. A continua- cion, y sobre la lista, encontrar los valores maximo y minima, y escribirlos. # include <stdio.h> # include <stdlib.h>
  • 166. main( ) ( float a[DIM~AXJ, max, min; int numval = 0, i; / * numval = nOde valores leidos */ / * Entrada de datos */ system("cls' '); printjt'Introducir valores y jinalizar pulsando AZ. n"); printf("a[%dJ = ': numval); while (numval < DIM~AX && scanf("%f': &a[numval]) 1= EOF) ( numval+ +; printf("a[%dJ = ': numval); I / * Encontrar los valores maximo y minimo */ if (numval > 0) ( max = min = a[OJ; for (i = 0; i < numval; i+ +) ( if (a[i] > max) max = a[i]; if (a[iJ < min) min = a[i]; I / * Escribir resultados */ printf(" n nValor maximo: %g, valor minimo: %g n': max, min); I else printf(" n nNo hay datos. n"); Una cadena de caracteres es un array unidimensional, en el cual todos sus elementos son de tipo char.
  • 167. Un array de caracteres puede ser inicializado asigmindole un literal. Por ejemplo: Este ejemplo inicializa el array de caracteres cadena con cinco elementos (cadena[Oj a cadena[4/). El quinto elemento, es el canicter nulo ( 0), can el cual C finaliza todas las cadenas de caracteres. Si se especifica el tamafio del array de caracteres y la cadena asignada es mas larga que el tamafio especificado, se obtiene un error en el momen- to de la compilaci6n. Por ejemplo: Este ejemplo daria lugar a un mensaje de error, indicandonos que he- mos excedido los limites del array. Si la cadena asignada es mas corta que el tamafio del array de caracte- res, el resto de los elementos del array son inicializados a valor nulo ( 0). Este ejemplo declara el array denominado nombre_apellidos como una cadena de caracteres de longitud maxima 60. Este ejemplo declara el array denominado UsIa como un array de ca- denas de caracteres. Esto es, UsIa es un array de 100 filas, cada una de las cuales es una cadena de caracteres de longitud maxima 60. Antes de leer una cadena de caracteres, debe declararse el array de tipa char que la va a contener. La dimensi6n de este array debe corresponderse con el numero de caracteres maximo que puede contener la cadena, mas uno correspondiente al caracter nulo de terminaci6n. Por ejemplo, si que- remos leer un nombre de 40 caracteres de longitud maxima, debemos de- clarar el array de la forma siguiente:
  • 168. Para leer esta cadena de caracteres, podemos emplear la funcion scanf( ). En este caso, la variable nombre, no necesita ser precedida por el operador &, porque nombre es una direccion, la direccion de comienzo del array. Si se lee elemento a elemento, el operador & es necesario. Lafuncion gets( ) lee una linea de la entrada estandar, stdin, y la almacena en la variable especificada. Esta variable es un puntero a la cadena de ca- racteres leida. La variable var, contiene todos los caracteres tecleados, excepto el ca- racter nueva linea ( n), que es automciticamente reemplazado por el ca- dcter nulo ( 0), con el cual C finaliza toda cadena de caracteres. La funcion gets( ) devuelve un puntero al valor leido. Un valor nulo para este puntero, indica un error 0 una condicion de fin de fichero (eo!). La funcion gets( ), a diferencia de la funcion scanf( ), permite la en- trada de una cadena de caracteres formada por varias palabras separadas par espacios en blanco, sin ningun tipo de formato. Recordar que para scanf( ), el espacio en blanco actua como separador de datos en la entrada. La funcion puts( ) escribe una cadena de caracteres en la salida estandar stdout, y reemplaza el caracter nulo de terminacion de la cadena ( 0) por el caracter nueva linea ( n).
  • 169. La fundon puts( ) retorna un valor positivo si se ejecuta satisfactoria- mente; en caso contrario, retorna el valor EOP. # include <stdio.h> # include <conio.h > char linea[81]; char *pc; main( ) { printft'Introduce una cadena de caracteres: "); pc = gets(linea); printjt' nLa linea introducida es: n"); printj("%s n': linea); puts("Pulse una tecla para continuar"); getch( ); puts(" nLa escribo por segunda vez:"); puts(pc); } Las fundones scanf( ), getchar( ), y gets( ) tienen una caracteristica comtin: leen los datos requeridos de la entrada estandar referendada par stdin. Es necesario tener presente que los datos, cuando son tecleados, no son leidos directamente del dispositivo, sino que estos son depositados en una memo- ria inter media (buffer), asociada con el dispositivo de entrada. Los datos son leidos de la memoria intermedia cuando son validados; esto ocurre cada vez que pulsamos Enter. Recordar que las funciones scanf( ) y getchar( ) interpretan el caracter , n' (nueva linea) como un separador. En modo texto y bajo DOS, pulsar
  • 170. la tecla Enter equivale a transmitir dos caracteres: CR + LF (estos en UNIX equivalen a un solo canicter: ' n'; ver capitulo 8). Esto quiere decir que el canicter que actua como separador es CR; quedando en el buffer aso- ciado con stdin, el canicter LF. Este es un canicter valido para la funcion gets(), entre otras; 10 que quiere decir que cuando estas funciones se com- binan en un mismo programa, hay problemas de lecturas no deseadas. La solucion a esto es limpiar el buffer asociado con stdin despues de una lec- tura con las funciones scanj( ) 0 con getchar( ), y antes de una lectura con la funcion gets( ). # include <stdio.h> # include <stdlib.h> # include <ctype.h > main( ) I int entero; double real; char respuesta system("cls ''); / * Introducir mimeros */ printf("Introducir un nO entero y un nO real: n"); scanf("%d %!j': &entero, &real); printj("%d + %f = %f n n': entero, real, entero + real); / * Leer 4 cadenas de caracteres */ printf("Introducir 4 cadenas de caracteres con scan!' n"); for (entero = 0; entero < 4; entero + +) I scanj("%s': cadena); printf("%s n': cadena); 1 / * Limpiar el buffer de entrada y leer una cadena con gets */ fflush( stdin ); printj("Introducir cadenas para gets. n");
  • 171. while (respuesta = = is' && gets(cadena) != NULL) ( printj("%s n': cadena); do ( printjt'l. Desea continuar (sin) "); respuesta = getchar( ); J while ((respuesta!= is') && (respuesta!= en')); 1* Limpiar el buffer de entrada *1 fflush( stdin ); J J Este programa utiliza la funci6n scanf( ), para leer datos numericos y de caracteres. Segun 10 expuesto en el parrafo anterior, en el buffer aso- ciado por el sistema a stdin queda el caracter LF; a continuaci6n, este ca- racter sera leido par la funci6n gets( ), sustituyendo asi, ala primera cade- na que tendria que leer esta funci6n~ Este problema se evita limpiando el buffer de stdin con la funci6nfflush(), antes de realizar la lectura con gets( j. Con la funci6n getchar( ), nos ocurre el mismo problema que con scanf( j. Examinar una cadena de caracteres. Leer una cadena de caracteres y, a continuaci6n, escribir por cada caracter, su direcci6n, el caracter y su valor ASCII. # include <stdio.h > # include <stdlib.h > main( ) { char cadena[41]; iot i = 0;
  • 172. system (<tcls''),o puts(<tEscriba una cadena de caracteres: "),o gets(cadena); / * Examinar cadena d do [ printf(<tDirecci6n = 0/05u cardcter= '%c' c6digo ASCII = 0/04d n': &cadena[ij, cadena~h cadena~]k i+ +,o J while (cadena[i]!= ' 0'); J Escribir un programa, que uti lice una funci6n para leer una linea de la entrada y que de como resultado, la linea leida y su longitud 0 numero de caracteres. # include <stdio.h> # include <stdlib.h> # define CARS~INEA 81 int leer_linea(char linear J),o main( ) [ char linea[CARS~INEA],o int long_linea = 0; / * array de caracteres */ / * longitud de una /{nea leida */ system("cls"),o / * limpiar pantalla */ puts("I ntroducir texto: "),o / * Se llama a la funci6n leer_linea, pasando como pardmetro * el array linea. Un array siempre es pasado por referencia. */ long_linea = leer_linea(linea),o
  • 173. printj(H nLinea leida: n"); puts(linea); printj(HLongitud: %d n': long_linea); J /************************************************************** Funci6n leer lfnea **************************************************************/ / * Lee una linea (linea[CARS-.LINEAJ) y calcula su longitud */ int leer_linea (char linear J) [ int long_linea = 0; while ((linea[long_lineaJ = getchar( )) != f n' && long_linea < CARS-.LINEA-l) + + long_linea; / * Si la entrada excede la longitud maxima, se trunca. En * cualquier caso la cadena se jinaliza con el caracter nulo * t0'), convenio utilizado por C. */ linea[long_lineaJ = f 0'; return long_linea; J Realizar la funci6n anterior' 'leer linea" utilizando ahora las funcio- nes gets( ) y puts( ). /************************************************************** Funci6n leer linea **************************************************************/ / * Lee una linea (linea[CARS-.LINEAJ) y calcula su longitud */ int leer_linea (char linear J) [ int i = 0;
  • 174. gets(linea); while (linea[i + +] /= < 0 '); return (i-I); J Escribir un programa que lea una linea de la entrada y la almacene en un array de caracteres. A continuaci6n, utilizando una funci6n, conver- tir los caracteres escritos en mayusculas, a minusculas. # deJine LONG--.MAX 81 ·1* longitud maxima de la cadena *1 void Mayusculas_minusculas(char str[ J); main() 1* Juncian principal *1 ! char cadena[LONG--.MAX]; int i = 0; printj ("Introducir cadena: "); while ((cadena[i] = getchar( )) /= < n' && i < LONG--.MAX - 1) + +i; cadena[i] = < 0'; Mayusculas_minusculas(cadena); 1* llamada a la Juncian *1 printj ("%s n': cadena); J /************************************************************** Funcian Mayusculas_mimisculas **************************************************************/ / * Convierte mayusculas a minusculas *1 void Mayusculas_minusculas(char str[ J) ! int i;
  • 175. for (i = 0; str[i]!= ' 0'; + +i) if (str[i) > = :4' && str[i] < = '2') str[i] = str[i] + 'a' - :4'; Observar que cuando dimensionamos un array de caracteres, por ejem- plo a un valor de LONG-.MAX, los indices de los elementos disponibles van desde 0 a LONG-.MAX-1. En el ejemplo vemos que si la entrada ex- cede la longitud maxima establecida, la cadena se trunca y se finaliza con caracter nulo (' 0'), convenio utilizado por C. Un array de cadenas de caracteres es un array donde cada elemento es a su vez un array de caracteres. Dicho de otra forma, es un array de dos di- mensiones de tipo char. El siguiente ejemplo, lee una lista de nombres y los almacena en un array. # include <stdio.h > # include <stdlib.h> #define MAX 10 #define LONG 60 / * maximo mimero de nombres */ / * mimero de caracteres maximo por nombre */ main( ) [ char lista[MAXJ[LONG]; char 4in; / * valor devuelto por gets */ iot i = 0, n; / * Indices */
  • 176. puts(HParajinalizar la entrada pulse etrl +Z (EOF)."); do ( printf(H nNombre y Apellidos: "); / * Un EOF hace que gets devuelva un puntero nulo (NULL) */ fin = gets(lista[i+ +J); J while (fin != NULL && i < MAX); / *Escribir datos */ printf(H n n"); for (n = 0; n < i; n + +) printft'%s n': lista[n)); Lasdeclaraciones de las funciones, para manipular cadenas de caracteres, que a continuacion se describen, estan en el fichero a incluir string.h. La sentencia: Esta fundon afiade la cadena2 a la cadenal, termina la cadena resultante con el caracter nulo y devuelve un puntero a cadena!.
  • 177. Esta funci6n devuelve un puntero a la primera ocurrencia de c en cadena o un valor NULL si el canicter no es encontrado. EI canicter c puede ser un canicter nulo (' 0'). <0 si la cadena! es menor que la cadena2, =0 si la cadena! es igual a la cadena2 y >0 si la cadena! es mayor que la cadena2. Esta funci6n copia la cadena2, incluyendo el canicter de terminaci6n nulo, en la cadenal y devuelve un puntero a cadenal. Esta funci6n da como resultado la posici6n (subindice) del primer canicter de cadenal, que pertenece al conjunto de caracteres contenidos en cadena2. Este valor corresponde a la longitud de la subcadena de cadenal formada por caracteres no pertenecientes a cadena2. Si ningun canicter de cadena! pertenece a cadena2, el resultado es la posici6n del canicter de terminaci6n ( 0) de cadenal; esto es, la longitud de cadenal.
  • 178. Esta funcion devuelve la longitud en bytes de cadena, no incluyendo el ca- racter de terminacion nulo. # include <stdio.h> # include <string.h> main( ) [ char cadenal[80]; static char cadena2[80] int n; char *r; strcpy(cadenal, "abc"); r = strcat(cadenal, "dej"); printj("%s n': r); / * escribe: abcdej */ r = strchr(cadenal, Cd'); printf("%s n': r); / * escribe: dej */ n = strcmp(cadenal, cadena2); printf(" "%s " es %s "%s " n': cadenal, n ? (n > 0 ? "mayor que" : "menor que") : "igual a':cadena2); / * escribe: "abcdej" es men or que "xyz" */ n = strcspn(cadenal, "edc"); printf("La posicion de e, doc en %s es %d n': cadenal, n); / * escribe: La posicion de e, doc en abcdej es 2 */ printf("EI tamano de cadena2 es %d n': strlen(cadena2)); / * escribe: El tamano de cadena2 es 3 */ 1
  • 179. Esta funci6n aiiade los primeros n caracteres de cadena2 a la cadenal, ter· mina la cadena resultante con el canlcter nulo y devuelve un puntero a cadenal. Si n es mayor que la longitud de cadena2, se utiliza como valor de n la longitud de cadena2. Esta funci6n compara los primeros n caracteres de cadenal y cadena2, dis· tinguiendo mayusculas y minusculas, y devuelve un valor: <0 si la cadena! es menor que la cadena2, =0 si la cadena! es igual a la cadena2 y >0 si la cadena! es mayor que la cadena2. Si n es mayor que la longitud de la cadenal, se toma como valor la longitud de la cadenal. Esta funci6n copia n caracteres de la cadena2, en la cadenal (sobreescri· biendo los caracteres de cadenal) y devuelve un puntero a cadenal. Si es menor que la longitud de cadena2, no se aiiade automaticamente un ca racter nulo a la cadena resultante. Si n es mayor que la longitud de cadena2 la cadenal es rellenada con caracteres nulos (' 0') hasta la longitud n, Esta funci6n devuelve un puntero a la ultima ocurrencia de c en cadeni o un valor NULL si el caracter no es encontrado. El caracter c puede s un caracter nulo (' 0'). '
  • 180. Esta funci6n da como resultado la posici6n (subindice) del primer canlcter de cadenal, que no pertenece al conjunto de caracteres contenidos en cadena2. Esto es, el resultado es la longitud de la subcadena inicial de cadenal, formada por caracteres pertenecientes a cadena2. Esta funci6n devuelve un puntero a la primera ocurrencia de cadena2 en cadenal 0 un valor NULL si la cadena2 no se encuentra en la cadenal. Esta funci6n lee la cadenal como una serie de cero 0 mas elementos basi- cos separados por cualquiera de los caracteres expresados en cadena2, los cuales son interpretados como delimitadores. Una vez leido el primer elemento de cadenal, la siguiente Hamada a strtok( ), para leer el siguiente elemento, se efectua poniendo NULL en lu- gar del argumento cadenal. Observar el ejemplo que se expone a conti- nuaci6n. Esta fund6n devuelve un puntero por cada elemento en cadenal. Cuan- do no hay mas elementos, se devuelve un puntero nulo. Puede ponerse mas de un delimitador entre elemento y elemento. Tam- bien pueden variarse el conjunto de caracteres que actuan como delimita- dores, de una Hamada a otra.
  • 181. # include <stdio.h> # include <string.h > char *cadena = Cfestacadena, estd jormada por varias palabras"; char ~lemento; main( ) { elemento = strtok(cadena, Cf ,"}; while (elemento != NULL) { printj(Cf%s n': elemento}; elemento = strtok(NULL, Cf , "}; esta cadena esta formada par varias palabras Los argumentos empleados en estas funciones, cadena, cadena! y cadena2, deben contener como caracter de terminaci6n el caracter nulo (' 0'). Esta funci6n asigna memoria para almacenar cadena, copia cadena en el espacio de memoria asignado y devuelve un puntero a la cadena copiada.
  • 182. Esta funcit'>nda como resultado el mensaje de error correspondiente al nu- merode error oro_error. Este numero de error normalmente viene dado por la variable predefinida errno. Conviertelas letras mayusculas de cadena, en minusculas. El resultado es la propia cadena en minusculas. Conviertelas letras minusculas de cadena, en mayusculas. El resultado es la propia cadena en mayusculas. Estafunci6n sustituye todos los caracteres de cadena, por el canicter c, ex- cepto el canicter de terminaci6n O.
  • 183. Cuando las funciones atof( ), atoi( ) y atol( ) leen un canicter que es reconocido como parte de un numero, dejan de leer caracteres de la riable cadena. # include <stdio.h > # include <stdlib.h >
  • 184. main( ) ! char *cadena = "-123.45E+05"; double r; r = atof(cadena); printf("%E': r); l Convierte un numero real a una cadena de caracteres. La cadena de carac- teressera finalizada con el caracter nulo. Esta funci6n devuelve un punte- ro a la cadena de caracteres. decs numero de digitos despues del punta decimal. Si es necesario, se afiaden ceros. pdec devuelve un puntero a un valor entero que especifica la posi- ci6n del punto decimal. signo devuelve un puntero a un valor 0, si el numero es positivo; 0 un valor distinto de 0, si el numero es negativo. Convierteun valor entero a una cadena de caracteres. La cadena de carac- teressera finalizada con el caracter nulo. Esta funci6n devuelve un punte- ro a la cadena de caracteres.
  • 185. es la base (2 - 36) en la que viene especificado el valor. Si la base es 10 y el valor negativo, el primer canicter almacenado en cadena es el signa menos. Convierte un valor entero en formato largo, a una cadena de caracteres. La cadena de caracteres sera finalizada con el caracter nulo. Esta fundon devuelve un puntero a la cadena de caracteres. es la base (2 - 36) en la que viene especificado el valor. Si la base es 10 y el valor negativo, el primer caracter almacenado en cadena es el signa menos. # include <stdio.h> # include <stdlib.h> main( ) { double valor = 3.1415926; int decimales = 8, punto_d, signo; char *cadena;
  • 186. cadena = jcvt(valor, decimales, &punto_d, &signo); printf("cadena = %s n': cadena); printj("signo = %d n': signo); printjt'posici6n punta decimal = %d n': punto_d); J cadena = 314159260 signa = 0 pasici6n punta decimal 1 FUNCIONES PARA CLASIFICACION Y CONVERSION DE CARACTERES Las declaraciones para las funciones, que a continuaci6n se describen, para clasificar y convertir caracteres, estan en el fichero a incluir ctype.h ( # include <ctype.h ».
  • 187. Comprueba si c es un canicter de control (0 - 31 y 127). int iscntrl(int c); Comprueba si c es un digito ('0' - '9'). int isdigit(int c); Comprueba si c es un canicter escribible, exceptuando el espacio (33 - 126). int isgraph(int c); Comprueba si c es una letra minuscula ('a' - 'z'). int islower(int c); isprint(c) Comprueba si c es un caracter escribible (32 - 126). int isprint(int c); Comprueba si c es un caracter de puntuaci6n. int ispunct(int c);
  • 188. Todas estas funciones devuelven un valor distinto de 0 si se satisface la condici6n, y 0 en caso contrario. Pone a 0 todos los bits de c, excepto los 7 bits de menor orden. Dicho de otra forma, convierte c a un canicter ASCII.
  • 189. Las funciones toascii( ), tolower( ) y toupper( ) devuelven el canicter convertido, si procede. Las funciones tolower( ) y toupper( ) tambien estan definidas en stdlib.h. # include <stdio.h> # include <ctype.h > # include <stdlib.h> main( ) [ int c; char car; system("cls"); for (c = 0; c < 128; c+ +) [ printj("Cardcter%4d %c %s': c, iscntrl(c) ? ' x20' : c, isascii(c) ? "ASCII" : ""); printf("%3s': iscntrl(c) ? "C" : ""); 1* Control *1 printj("%4s': isalnum(c) ? "AN" : ""); 1* AljaNumcfrico *1 printf("%3s': isalpha(c) ? '~" : ""); 1* Aljabetico *1 printj("%3s': ispunct(c) ? "P" : ""); 1* Puntuaci6n *1 putchar(' n'); if (c % 20 = = 0 && c != 0) [ printj(" nControl, AljaNumerico, Aljabetico, Puntuaci6n "); printj(" n n/,Desea continuar? sin "); car = getche( );
  • 190. if (tolower(car) break; else system (Hcls"); } } } Una estructura es un nuevo tipo de Idatos que puede ser manipulado de la misma forma que los tipos predefinidos como float, int, char, entre otros. Una estructura se puede definir como una coleccion de datos de diferent~s tipos, logicamente relacionados. En C una estructura solo puede contener dec1aracionesde variables. En otros compiladores, este tipo de construc- danes son conocidas como registros. Crear una estructura es definir un nuevo tipo de datos, denominado tipo estructura y declarar una variable de este tipo. En la definicion del tipo estructura, se especifican los elementos que la componen as! como sus ti- pas. Cada elemento de la estructura recibe el nombre de miembro (campo del registro). La sintaxis es la siguiente: struct tipo_estructura { declaraciones de los miembros }; Despues de definir un tipo estructura, podemos declarar una variable de ese tipo, de la forma:
  • 191. Para referirse a un determinado miembro de la estructura, se utiliza la notaci6n: struct Jicha / * dejinicion del tipo estructura Jicha */ [ char nombre[40); char direccion[40); long telejono; ]; Este ejemplo define las variables varl y var2, de tipo!icha, por 10 que cad a una de las variables consta de los miembros: nombre, direccion y te- lejono. Una variable que es un miembro de una estructura, puede utilizarse exactamente igual que cualquier otra variable. varl.telejono = 232323; gets(var2.nombre); La declaraci6n de las variables varl y var2, puede realizarse tambien directamente de la siguiente forma: struct Jicha [ char nombre[40); char direccion[40); long telejono; ] varl, var2; o tambien, sin dejar constancia del nuevo tipo definido, forma que no se aconseja:
  • 192. struct ( char nombre[40); char direccion[40}; long telejono; ] varl, var2; La declaracion de un miembro de una estructura no puede contener calificadores de clase de almacenamiento extern, static, auto 0 register y no puede ser inicializado. Su tipo puede ser: fundamental, array, puntero, union, estructura 0 funcion. Para declarar un miembro como una estructura, es necesario haber declarado previamente ese tipo de estructura. En particular una estructura st no puede contener un miembro de tipo st, pero sf puede contener un puntero 0 referencia a un objeto de tipo st. struct jecha ( int dia, mes, anyo; ]; struct Jicha ( char nombre[40); char direccion[40}; long telejono; fecha jecha_alta; ]; Este ejemplo define la variable persona, en la que el miembro jecha_alta es a su vez una estructura. Los miembros de una estructura son almacenados secuencialmente, en el mismo orden en el que son declarados.
  • 193. Con una variable declarada como una estructura, pueden realizarse las si- guientes operaciones: • coger su direcci6n por medio del operador & • acceder a uno de sus miembros • asignar una estructura a otra con el operador de asignaci6n. Cuando los elementos de un array son de tipo estructura, el array recibe el nombre de array de estructuras 0 array de registros. Esta es una cons- trucci6n muy uti! y potente. El siguiente programa lee una lista de alumnos y sus correspondientes notas de final de curso, dando como resultado el tanto por ciento de alum- nos aprobados y suspendidos. # include <stdio.h> # include <stdlib.h> # deJine NA 10 / * mimero maximo de afumnos */ main( ) { struct Jicha { char nombre[60]; float nota; }; struct Jicha afumnos[NA]; / * array de estructuras 0 registros */ iot n = 0, i; char 4in; float aprobados / *puntero af nombre feldo */ 0, suspensos = 0;
  • 194. / * Entrada de datos */ system (HclsH); printj(HFinalizar la entrada de datos con AZ n nH ); printj(HNombre H); fin = gets(alumnos[n}.nombre); while (n < NA && fin != NULL) { printj(HNota H); scanj(H%f': &alumnos[n + +}.nota); fflush(stdin); / * eliminar el retorno de carro */ printj(HNombre H); fin = gets(alumnos[n}.nombre); ) / * Escribir resultados */ system(Hcls H); for (i = 0; i < n; i+ +) if (alumnos[i}.nota > = 5) aprobados+ +; else suspensos + +; printft'Aprobados %.2g %% n': aprobados/n*100); printj("Suspensos %.2g %% n': suspensos/n *100); ) Una union es una variable que puede contener miembros de diferentes ti- pos, en una misma zona de memoria. La declaraci6n de una union tiene la misma forma que la declaraci6n de una estructura, excepto que en lugar de la palabra reservada struct se pone la palabra reservada union. Todo 10 expuesto para las estructuras, es aplicable alas uniones, excepto la forma de almacenamiento de sus miembros.
  • 195. declaraciones de los miembros }; union tipo_union { Despues de definir un tipo uni6n, podemos declarar una 0 mas varia- bles de ese tipo de la forma: Para referirse a un determinado miembro de la uni6n, se utiliza la no- taci6n: Para almacenar los miembros de una uni6n, se requiere una zona de memoria igual a la que ocupa el miembro mas largo de la uni6n. Todos los miembros son almacenados en el mismo espacio de memoria y comien- zan en la misma direcci6n. El valor almacenado es sobreescrito cada vez que se asigna un valor al mismo miembro 0 a un miembro diferente. union tipo_union { char varl; int var2; float var3; }; Este ejemplo declara una variable var_union que representa una union con tres miembros. Esta variable debe ser 10 suficientemente grande como para contener el mayor de los tres miembros. Cualquiera de los tres miem- bros pueden asignarse a var_union y utilizarse en expresiones. Es respon- sabilidad del programador recordar cual es el miembro que hay en la union. El esquema siguiente, expresa c6mo se ve graficamente var_union.
  • 196. var_union I I I I E varl -l ~ .1 var2 var3 Si en lugar de haber declarado una union hubiesemos declarado una estructura, los miembros sedan individuales, esto es: struct /ibro I unsigned edicion; unsigned anno; J; union /ibro-revista I struct /ibro /ibros; char nomrev[30}; J; structJicha I unsigned numreJ; char titu!o[30};
  • 197. char autor[20]; char editor[25]; int clase-publicacion; union libro---fevista lr; ]; Este ejemplo, define una estructura variable, apoyandose en una va- riable lr de tipo union. Esta variable contendra, 0 bien los datos edicion y anno, 0 bien nomrev. Consideremos una biblioteca compuesta por libros y revistas. Por cada libro 0 revista, figura la siguiente informacion: Numero de referencia. TItulo. Nombre del autor. Editorial. Clase de publicacion (libro 0 revista). Numero de edicion (solo libros). Ano de publicacion (solo libros). Nombre de la revista. 1. Almacenar en un array la informacion correspondiente a la bi- blioteca. # include <stdio.h > # include <stdlib.h > # include <ctype.h > # define N 10
  • 198. enum clases ( libra, revista J; struct Jicha 1* registro variable *1 ( unsigned numreJ; char titulo[30]; char autor[20]; char editor[25]; enum clases libro-'evista; union ( struct ( unsigned edicion; unsigned anno; J libros; char nomrev[30]; J Ir; J; main( ) 1* Junci6n principal *1 ( void escribir(int); void leer(int); int k = 0; char resp = 's'; while (tolower(resp) = = 's' && k < N) ( leer(k + +); I * leer un registro *1 printj(" n n;. Mas datos a introducir ? sin "); resp = getche( ); J escribir(k); 1* listar todos los registros *1 J
  • 199. 1************************************************************** Funci6n para leer un registro (Jicha) **************************************************************/ void leer(int k) { system (Hcls "); printjt'INTRODUCIR DATOS n n"); printf(HNtimero de refer. "); scanft' % u' :&bibli[k].numref); fflush(stdin); printj(HTftulo "); gets(bibli[k].titulo); printft'Autor "); gets(bibli[k].autor); printf(HEditor "); gets(bibli[k].editor); printj(HLibro 0 revista (0 libro, 1 = revista) "); scanf(H%d': &clase);fflush(stdin); if (clase = = libro) { bibli[k}.libro_revista = libro; printf(HEdici6n "); scanf(H % u': &bibli[k}.lr.libros.edicion); printf(''Ano de public. "); scanft '%u': &bibli[k}.lr.libros.anno); l else { bibli[k].libro-.Jevista = revista; printj(HNombre revista "); gets(bibli[k}.lr.nomrev); l l 1************************************************************** Funci6n para listar todos los registros **************************************************************/ void escribir(int totaLJichas) { int k; system(Hcls"); printf(HLISTADO DE LIBROS Y REVISTAS n");
  • 200. for (k = 0; k < totaL../ichas,o k + +) [ printj(H n "),o printf(H%d %s n': bibli[k].numref, bibli[k].titulo),o printj(H%s - Ed. %s n': bibli[k].autor, bibli[k].editor),o switch (bibli[k].libro_revista) [ case libro : printjt'Edici6n %u - ano %u n': bibli[k].lr.li bros.edic ion, bibli[k].lr.libros.anno ),o break; case revista : printj(H%s n': bibli[k].lr.nomrev),o J J J Un campo de bits es un conjunto de bits adyacentes dentro de una unidad direccionable. Una declaracion de un campo de bits tiene la forma: La expresi6n constante especifica e1numero de bits en el campo y debe ser un valor entero no negativo. EI tipo tiene que ser entero (signed 0 un- signed). No se permiten arrays de campos de bits, punteros a campos de bits 0 funciones que retornen un campo de bits. EI nombre de un campo es opcional. Los campos sin nombre sirven de relleno. Es posible definir como miembros de una estructura (no de una union) campos de bits. EI tamafio de un campo de bits no debe sobrepasar el ta- mafio flsico de la palabra maquina, es decir, e1 espacio ocupado por un entero. Si esto ocurre, el campo siguiente se alinea con respecto al siguiente entero. Un campo de bits sin nombre y de tamafio 0, garantiza que el si- guiente miembro de la lista comience en e1siguiente espacio para un entero.
  • 201. struct palabra / *palabra de 16 bits - 0 a 15 */ { unsigned car_ascii : 7; / * bits 0 a 6 */ unsigned bit-paridad: 1; / * bit 7 */ unsigned operacion : 5; / * bits 8 a 12 */ unsigned : 2; / * bits 13 a 14 de rel/eno d unsigned bit-signo : 1; / * bit 15 */ }; main( ) / *junci6n principal */ { printj("campo printjt'bit de signa printf("operaci6n printj("bit de paridad printjt'cardcter ASCII } %x n n': campo); %x n': campo.bit-signo); %x n': campo.operacion); %x n': campo.bit-paridad); %x n': campo.car_ascii); bit de signa : 0 aperaci6n : Ie bit de pari dad : 1 canlcter ASCII : 43 15 14 13 12 11 10 9 8 7 10 0 0 111 1 1 011 5 4 3 2 o 010 0
  • 202. La alineaci6n de los campos de bits depende de la implementaci6n. Los campos son asignados de izquierda a derecha 0 de derecha a izquier- da, caso del ejemplo mostrado, segun la maquina que se emplee. Para comprender las ventajas de la utilizaci6n de los campos de bits, pensemos en construir una estructura para contener la fecha. Tal estructu- ra puede ser la siguiente: struct jecha [ unsigned dia; unsigned mes; unsigned anyo; ); Esta estructura requiere un total de seis bytes. En cambio, si utiliza- mas campos de bits, podemos representar esa misma estructura mucho ma:; camprimida: struct jecha [ unsigned dia : 5; unsigned mes : 4; unsigned anyo: 7; j; Como ya se ha explicado anteriormente, podemos acceder a los miem- bras individuales de la estructura utilizando el operador pun to, como se ve en el siguiente ejemplo: main( ) [ struct jecha [ unsigned dia : 5; unsigned mes : 4;
  • 203. unsigned anyo: 7; ]; struct jecha hoy; hoy.dia = 18; hoy.mes = 4; hoy.anyo = 91; printj(C<%u/%u/%u n': hoy.dia, hoy.mes, hoy.anyo); ] Escribir un programa que de como resultado (en forma de tabla) las frecuencias de parejas de letras adyacentes, de un texto introducido por el teclado.
  • 204. ~ cual se contemplan todas las parejas posibles de letras, desde la aa hasta la zz. #include <stdio.h> #include <stdlib.h> #include <etype.h > #dejine DIM ('z' - ca' int tabla[DIM][DIM]; + 1) / *jilas/columnas de la tabla */ / * tabla de eontingencias */ main( ) I char f, e; char car; char earant; / * earaeter actual */ / * earaeter anterior */ / * Poner tabla a eeros */ for if = 0; j < = cz' - ca'; j+ +) for (e = 0; e < = cz' - ca'; e+ +) tablaU][e] = 0; system ("cls "); printf("Introdueir texto. Finalizar con A Z. n n "); carant = c'; while ((car = tolower(getehar( ))) != EOF) I if ((earant > = ca' && earant < = Cz') && (car> = ca' && car < = Cz')) tabla[earant - ca'j[ear - ca'j tabla[earant - ca'j[ear - ca'j + 1; carant = car; / * Escribir tabla de jreeuencias */ system("cls "); printf(" "); for (c = ca'; e < = cz'; e+ +) printf(" %e': c); putchar(' n ');
  • 205. putchar(f); for (c = 'a:' c < = 'z'; c+ +) printf(H%3d': tablaU - 'a'][c - 'a']); putchart n'); ~ for if = 'a'; f < = 'z'; f+ +) { EI siguiente program a presenta en pantalla una ventana, rellenada con el canicter tecleado y con posibilidad, a traves de un menu, de presentarla en texto subrayado, en alta intensidad, parpadeando, en video inverso, nOf. mal 0 con una combinacion de estas opciones. Bajo MS-DOS, la memoria intermedia de pantalla con adaptador mo- nocromo es de 4000 bytes, localizados a partir de la direccion 0 del seg- mento OxBOOO.La memoria intermedia de pantalla con adaptador color graficos (CGA) es de 4000 bytes de longitud en modo texto y de 16384 bytes de longitud en modo gnifico, localizados, en ambos casos, a partir de la direccion de memoria 0 del segmento OxB800. EI atributo de representacion en pantalla se localiza en la parte alta de la palabra y el canicter a representar, en la parte baja. Notar que no se puede asignar a un campo de bits, un valor que exce- da su capacidad. Por ejemplo, en el siguiente programa, el valor maximo para el campo de 3 bits primer_plano, es 7 (111 en binario). # include < stdio.h > # include < stdlib.h > # include <ctype.h >
  • 206. Y Las siguientes constantes definen la ventana a utilizar * dentro de la pantalla */ #define IFILA 11 / *primera fila de la ventana */ #define SFILA 20 / * ultima fila */ #define ICOLN 21 / *primera columna */ #define DCOLN 60 h ultima columna */ void escribir(char car, char atributo); main( ) [ struct atributos [ unsigned int primer ---plano unsigned int intensidad unsigned int color-Jondo unsigned int parpadeo J; struct atributos atributo; char ~trib = (char *)&atributo: char car; system("cls n); printft'Escriba: printj(" printj(" printf(" printf(" printf(" : 3; h bits 0 a 2 */ : 1; / * bit 3 */ : 3; h bits 4 a 6 */ : 1; / * bits 7 */ 's' -- > subrayado, nn); 'i' -- > int~nsidad, nn); 'p' -- > parpadeo, n n); 'v' -- > vldeo inverso, nn); 'n' -- > vuelta a normal, nn); 'x' -- > salir. nn); car = getch( ); while (tolower(car) != 'x') [ switch (car) [ . case's': atributo.primer ---plano break; case T: atributo.intensidad break; / * subrayado */ 1; / * alta intensidad d (atributo.intensidad = = 1) ? 0 : 1;
  • 207. case 'p': atributo.parpadeo break; case 'v': / * v(deo inverso */ atriblJto.primer ~lano 0; atributo.color -fondo = 7; break; case 'n': / * parpadeo */ (atributo.parpadeo = = 1) ? 0 : 1; atributo.primer ~lano 7; atributo.color -fondo = 0; break; l escribir(car, ~trib); car = getch( ); Rellenar la pantalla con el caracter car **************************************************************/ void escribir(char car, char atributo) { iot far *p; iot fila, col; p = (iot far *) OxB8000000; / * asignar a p B800:0000 */ for (fila = IFILA; fila < = SFILA; fila + +) for (col = ICOLN; col < = DCOLN; col+ +) 4p + fila * COLS + col) = car I atributo < < 8; En este programa, la estructura atributo contiene los atributos de los caracteres a representar en la ventana. Estos atributos tienen que localizar- se en la parte alta de la palabra de memoria correspondiente al canicter a representar. De ahi, el ejecutar la sentencia:
  • 208. la ~almacena en la palabra de direcci6np+jila*COLS+col, el canlcter en la parte baja y los atributos en la parte alta. La variable p (puntero) contiene la direcci6n de comienzo de la pantalla, esto es, la direcci6n co- rrespondiente a la fila 1 columna 1. Para acceder a una direcci6n fuera del segmento de datos en el cual estamos trabajando, utilizamos direcciones segmentadas. Para asignar un valor hexadecimal como una direcci6n segmentada, utilizaremos la pala- bra clave far. Como ejemplo, observar la sentencia: la cual indica, que la funci6n escribir debe recibir dos valores de tipo char: cary atributo. El argumento atributo en esta funci6n es de tipo entero, por- que necesitamos realizar sobre el un desplazamiento. Por ello, la llamada a la funci6n escribir tendrfa que ser de la forma: la cual da lugar a un error, ya que atributo en la funci6n main( ) es una estructura. Para salvar este inconveniente, recordar que un objeto de un determi- nado tipo, puede ser convertido a otro tipo cualquiera, utilizando conver- siones explicitas de tipo sobre punteros (ver Conversion explicita del tipo de una expresion, en el capitulo 2). De ahi, la instrucci6n:
  • 209. ~ / ** .•******** Manipulacion de un valor float bit a bit *********** main( ) { struct sfl { unsigned bO : 1; unsigned b1 : 1; unsigned b2 : 1; unsigned b3 : 1; unsigned b4 : 1; unsigned b5 : 1; unsigned b6 : 1; unsigned b 7 : 1; unsigned b8 : 1; unsigned b9 : 1; unsigned b10: 1; unsigned bll: 1; unsigned b12: 1; unsigned b13: 1; unsigned b14: 1; unsigned b15: 1; unsigned b16: 1; unsigned b17: 1; unsigned b18: 1; unsigned b19: 1; unsigned b20: 1; unsigned b21: 1; unsigned b22: 1; unsigned b23: 1; unsigned b24: 1; unsigned b25: 1; unsigned b26: 1; unsigned b27: 1; unsigned b28: 1; unsigned b29: 1; unsigned b30: 1; unsigned b31: 1; }; union ufl { float x; struct sfl s; }; real.x = -10.5; printj((real = "); printj((%d': real.s.b31); printf((%d': real.s.b30); printf((O/Od': real.s.b29); printft'%d': real.s.b28); printft'%d': real.s.b27); printj((%d': real.s.b26); printjt'%d': real.s.b25); printj((O/Od': real.s.b24); printjt'%d': real.s.b23); printj((%d': real.s.b22);
  • 210. printj2.'%d': real.s.b2I);printj("%d': real.s.b20); print]("%d': real.s.bI9);printf("%d': real.s.bI8); printj("%d': real.s.b17);printf("%d': real.s.bI6); printj("%d': real.s.bI5);printj("%d': real.s.bI4); printj("%d': real.s.bI3);printj("%d': real.s.b12); printf("%d': real.s.bll);printj("%d': real.s.bIO); printf("%d': real.s.b9); printj("%d': real.s.b8); printf("%d': real.s.b7); printj("%d': real.s.b6); printf("%d': real.s.b5); printj("%d': real.s.b4); printf("%d': real.s.b3); printj("%d': real.s.b2); printj("%d': real.s.bI ); printj("%d n': real.s.bO); ) Signa: 1 S = 1 Exponente: 100 0001 0 E = 3 Mantisa: 010 10000000000000000000 M = 0.3125 Este resultado esta representado en coma flotante, bajo el formate es- tandar IEEE; el cual emplea, mantisa fraccionaria normalizada en signa y magnitud, y sin almacenar el bit impHcito que es igual 1. EI exponente esta representado en exceso 127. Par tanto, el valor viene dado por (-1)SX l.Mx2E-127 para 0<E<255. Aplicado a nuestro ejemplo, obtenemos:
  • 211. Un puntero es una variable que contiene la direccion de memoria, de un dato 0 de otra variable que contiene al dato. Quiero esto decir, que el pun- tero apunta al espacio ffsico donde esta el dato 0 la variable. Un puntero puede apuntar a un objeto de cualquier tipo, incluyendo estructuras, fun- ciones etc. Los punteros se pueden utilizar para crear y manipular estruc- turas de datos, para asignar memoria dinamicamente y para proveer el paso de argumentos por referencia en las llamadas a funciones. Un puntero se declara precediendo el identificador que referencia al pun- tero, por el operador de indireccion (*), el cual significa "puntero a". Un puntero siempre apunta a un objeto de un tipo particular. Un puntero no inicializado tiene un valor desconocido. Especifica el tipo del objeto apuntado; puede ser cualquier tipo, incluyendo tipos agregados.
  • 212. var-pun~ro int *pint; char *pnom; double *p, *-Cj; / *pint es un puntero a un entero */ / * pnom es un puntero a una cadena de caracteres*/ / *p y q son punteros a reales */ EI espacio de memoria requerido para un puntero, es el numero de bytes necesarios para especificar una direccion maquina. En la familia de micros 8086, una direccion near (direccion con respecto a la direccion base del segmento) necesita 16 bits y una direccion far (direccion segmentada) necesita 32 bits. operador direccion de : & operador de indireccion: * El operador unitario &, devuelve como resultado la direccion de su operando. EI operador unitario *, toma su operando como una direccion y nos da como resultado su contenido. # include <stdio.h> main( ) { / * declaramos las variables enteras a, b y los punteros p y q * a enteros */ int a = 10, b, *P, *q; q = &a; / * asigna la direcci6n de a a la variable q */ / * q apunta a la variable entera a */
  • 213. b ~ *']; 1* asigna a b el contenido de la direccion q */ *P = 20; / * error: asignacion a un puntero no valida */ / * ~ a donde apunta p ? */ printj(C<Enla direccion %.4X esta el dato %d n': q, b); printf(C<Enla direccion %.4X esta el dato %d n': p, *p); } En la direccion OD96 esta el dato 10 En la direccion 54E6 esta el dato 20 ~ Como sabe C cuantos bytes tiene que asignar a una variable desde una direccion ? La respuesta es que C toma como referencia el tipo base del puntero y asigna el numero de bytes correspondientes a ese tipo. # include <stdio.h > main( ) [ float a = 10.5, b = 0; / * a y b son variables de tipo real */ p = &a; b=*p; printft'b l Al compilar este programa, se nos presentara el mensaje siguiente, de- bido a la sentencia: p = &a;
  • 214. OPER~IONES CON PUNTEROS # include <stdio.h > main( ) ( int a = 10, *P, *q; p = &a; q = p; / * la direcci6n que contiene p se asigna a q */ printf("En la direcci6n %.4X estd el valor %d': q, *q); } int *P; p++; p--; p = p + 3; p = p - 3; / * declam p como un puntero a un entero */ / * hace que p apunte al siguiente entero */ / * hace que p apunte al entero anterior */ / * avanzar tres enteros */ / * retroceder tres enteros */ Si p y q son variables tipo puntero y apuntan a elementos del mismo array, la operaci6n p - q es valida. No se permite sumar, multiplicar, divi- dir 0 rotar punteros y tampoco sumarles un real.
  • 215. Es posible comparar dos punteros en una expresion de relacion. Esta ope- racion tiene sentido si ambos punteros apuntan a elementos del mismo array. if (p + n < = q) P += n; if (q /= NULL && q < = p) q++; Los operadores unitarios * y & tienen mayor precedencia que los operado- res aritmeticos + y - e igual precedencia que + + Y --. Definimos dos variables enteras a y b, y dos punteros a datos de tipo entero pa y pb. al valor apuntado por pa se Ie suma 1, y el resultado se asigna a b. el siguiente entero al entero apuntado por pa, es asig- nado a b.
  • 216. la variable b se decrementa en una unidad. Sin paren- tesis, se decrementaria pb y no b. Un ejemplo interesante de punteros es un examen del contenido de la memoria. El siguiente programa permite visualizar byte a byte el conteni· do de la memoria RAM, a partir de una posici6n dada. Este programa introduce la palabra clavefar la cual nos proporciona una direcci6n segmentada, con la finalidad de poder acceder a posiciones de memoria fuera del segmento en el que nos encontramos. # include <stdio.h> # include <stdfib.h> # include <etype.h > # include < dos.h > main( ) / *funcion principal */ [ unsigned long de; / * direecion de eomienzo */ void explorar(unsigned long); system("cls' '); printf("Introducir la direecion de eomienzo "); seanf("%lx': &de); / *por ejemplo B8000000 */ explorar(de); / * llamada a la funcion explorar */ } void explorar(unsigned long de) [ unsigned char far *p; / *p eontiene una direecion segmentada, a un byte */ int e, fin; char r; p = (unsigned char far *)de; / * La notacion cast: (unsigned char far *) eonvierte el valor * de de a una direecion segmentada */
  • 217. do ( system("cls"); for (lin = 1; !in < = 16; !in+ +) / *pdginas de 16 lfneas */ ( printj("%04X'%04X ': FP_SEG(p), FP_OFF(p)); for (c = 1; c < = 16; c+ +) / * escribir 16 bytes */ printj("%02X ': *p+ +); / * escribir el contenido de p */ putchar{' n '); / * cambio de {{nea */ J printj(" n nPulse una tecla para continuar. S para sa!ir "); r = getch( ); J while (tolower(r) 1= 's'); / * jina!izar la exploraci6n cuando se pulse una tecla */ El programa utiliza una variable dc, de tipo unsigned long ya que una direcci6n de memoria excede del tamafio de un entero. Observar tambien el formato %lx que se utiliza con la funci6n scanf( ), para leer un entero formato largo en hexadecimal. Tambien utiliza un puntero p a un objeto de tipo unsigned char ya que el rango de valores para este tipo es de 0 a 255. Las funciones FP_SEG(p) y FP_OFF(p) nos dan como resultado la direcci6n del segmento y la direcci6n offset dentro de este, de la direcci6n far p. Esto permite, utilizando la notaci6n cast, asignar a la variable p, un puntero de cualquier tipo. Esto es a menu do utilizado en el contexto de llamadas a funciones.
  • 218. En C existe, entre punteros y arrays, una relaci6n tal que, cualquier opera- ci6n que se pueda realizar mediante la indexaci6n de un array, se puede realizar tambien con punteros. Para clarificar 10 expuesto, observar el siguiente programa, realizado primeramente con arrays y a continuaci6n con punteros. # include <stdio.h> main( ) { static int lista[ J = {24, 30, 15, 45, 34}; int ind; for (ind = 0; ind < 5; ind+ +) printf(H%d ': lista[ind]); En este ejemplo, la notaci6n utilizada para acceder a los elementos del array estatico, es la expresi6n: lista!indj. # include <stdio.h> main( ) { static int lista! J {24, 30, 15, 45, 34}; int ind; for (ind = 0; ind < 5; ind+ +) printf(H%d ': *(lista+ind)); Esta versi6n es identica a la anterior, excepto que la expresi6n pa acceder a los elementos del array es: *(lista+ind).
  • 219. Esto deja constancia de que /ista es la direccion de comienzo del array. Si a esta direccion Ie sumamos 1, 0 dicho de otra manera si ind vale 1, nos situamos en el siguiente entero de la lista; esto es *(/ista +l) y /ista[l] repre- sentan el mismo valor. Notar que un incremento de uno sobre una direc- cion equivale a avanzar no un byte, sino un numero de bytes igual al tama- no de los objetos que estamos tratando. Segun esto, hay dos formas de referirnos al contenido de un elemento de un array: indica la posicion de un elemento dentro del array. EI primer ele- mento ocupa la posicion 0 y el ultimo la posicion 0-1, siendo 0 el numero total de elementos. Puesto que *(/ista +0) es igual que /ista[O], la asignacion: p = &/ista[O] es la misma que la asignacion p = /ista, donde pes una variable de tipo puntero. Esto indica que la direccion de comienzo de un array es la misma que la del primer elemento. Por otra parte, despues de haber efectuado la asignacion p = /ista, las siguientes expresiones dan lugar a identicos re- sultados: Sin embargo, hay una diferencia entre el nombre de un array y un pun- tero. EI nombre de un array es una constante y un puntero es una variable. Esto quiere decir que las operaciones: !isla = p 0 lista+ + p = /ista 0 P+ + no son correctas, y las operaciones sf son correctas. Puesto que una cadena de caracteres es un array de caracteres, es correcto pensar que la teorfa expuesta anteriormente, es perfectamente aplicable a cadenas de caracteres.
  • 220. char *nombre = "Francisco Javier"; printj("%s': nombre); Este ejemplo asigna a nombre, la direccion de comienzo de la cadena de caracteres especificada. EI compilador afiade al final de la cadena, el canicter 0, para que en operaciones posteriores podamos detectar el fi- nal de la misma. Muchas funciones de C que trabajan con cadenas de caracteres, utili- zan punteros y devuelven como resultado un puntero. Por ejemplo, la fun- cion de C strchr( ) para localizar un canicter en una cadena, devuelve un puntero al canicter buscado 0 un puntero nulo si el canicter no se encuentra. EI siguiente ejemplo estudia posibles formas de calcular la longitud de una cadena de caracteres, utilizando punteros. main( ) [ static char *cadena = "abcd"; / * el cardcter de terminaci6n ' 0' es afiadido automdticamente */ printj("%d n': longstr(cadena)); l
  • 221. int longstr(char *-Str) I char *P = str; while (*P 1= t 0') P++; return ((int)(p - str)); ] La diferencia p - str nos da la longitud de la cadena. El bucle formado par la sentencia while, realiza las siguientes operaciones: 3. Si no es el canlcter nulo, se pasa a la siguiente direcci6n y se repite el proceso desde el punta 1. Puesto que la sentencia while s610 comprueba si la expresi6n *p 1= t 0' es cera, en lugar de hacer la comparaci6n explfcitamente, se puede hacer implicitamente as!: while (*p) P++; Ahara la condici6n est a formada por la expresi6n *p. Si esta expre- si6n es distinta de cero, la condici6n es cierta. En cambio si es cero, (carac- ter nula, ya que su valor ASCII es 0) la condici6n es falsa. Ahara el resultado vendra dado por la expresi6n p - str - 1, ya que despues de examinar si la condici6n es cero, se efectua la operaci6n + + . Teneren cuenta que si escribimos * + +p, primero se efectua la operaci6n + + sabre p y despues se examina el contenido de esa direcci6n. Esto no seria valida para cadenas de longitud 1.
  • 222. Siendo c una variable de tipo char, la expresion es la misma que y es equivalente a ) c *P+ +,o c *(P+ +),o c = *P,op+ +,o c *+ +P,o c *(+ +p),o + +P,oc = *P,o c ++ *P,o c + + (*p),o *p+=l,oc= *P, c = (*p) + +,o c = *p,o(*p) + +; El siguiente programa copia una cadena de caractereS en otra. Prime- ramente se presenta una version con arrays y despues otra con punteros. main( ) ( char cadenal[81], cadena2[81],o void copstr(char *, char *),o printft'Introducir cadena: "),o gets(cadenal),o copstr(cadena2, cadenal),o / * copia fa cadenal en fa cadena2 d printf(H n nLa cadena copiada es: %s n': cadena2),o l void copstr(char *P, char~) / * copia q en p */ { int i = 0; while ((p[i] i+ +,o
  • 223. void copstr(char *p, char..q) / * copia q en p */ [ while ((*p = ..q) != < OJ [ p++; q++; J J Si tras1adamos e1incremento de p y q a 1a expresi6n de 1a sentencia while, obtenemos esta otra versi6n: void copstr(char *P, char..q) / * copia q en p */ [ while ((*P+ + = ..q+ +)!= < 0'); J E1bucle formado por 1asenten cia while, realiza 1assiguientes opera- ciones: 1. Toma e1canicter contenido en 1adirecci6n dad a por q y 10copia en 1a direcci6n dada por p. 3. p pasa a apuntar a 1asiguiente direcci6n, y 10mismo sucede con q. Si no se dio 1a condici6n de terminaci6n, e1proceso se repite desde e1punta 1. Puesto que 1asentencia while s610comprueba si 1aexpresi6n *P != < 0' es cera, en 1ugar de hacer 1a comparaci6n explicitamente, se puede hacer implicitamente asi: void copstr(char *P, char..q) / * copia q en p */ [ while (*P + + = ..q+ +); J
  • 224. Cuando se pasa el nombre de un array a una funci6n, se pasa la direc- ci6n de comienzo del array, ya que el nombre del array es un puntero a dicho array; dicho de otra forma el paso de un array se hace por referencia y no por valor. Para hacer referencia a una cadena de caracteres, podemos utilizar un pun- tero 0 un array. Cuando se utiliza un puntero, podemos inicializar la variable de la for- ma siguiente: La version array asigna el numero suficiente de bytes para almacenar la cadena especificada, (en este caso 16) mas un byte para el carcicter de terminaci6n ' 0'. La direcci6n del primer caracter del array viene dada por el nombre del array; en este caso, por nombre. En la version puntero, la asignaci6n de bytes se efectua de la misma forma, pero ademas se asigna memoria para la variable puntero nombre, que es la que contiene la direcci6n de comienzo de la cadena. Esto quiere decir que con esta versi6n se ocupa mas memoria, pero tambien las venta- jas son mayores. En la versi6n array, nombre es un puntero constante; por 10 tanto, la direcci6n no puede ser modificada. En la versi6n puntero, nombre es una variable, por 10 que sf puede ser modificada. Esto es, admite operaciones como + + nombre, prohibida en la versi6n array.
  • 225. En capitulos anteriores, hemos trabajado con arrays multidimensionales, aunque en la practica 10 mas habitual es trabajar con arrays de punteros, cuesti6n que se expone a continuaci6n. Se puede definir un array, para que sus elementos contengan en lugar de un dato, una direcci6n 0 puntero. int *a[lOj, v; a[O] = &v; prin!f("%d': *-fl[O]); Este ejemplo define un array de 10 elementos que son punteros a da- tos de tipo in! y una variable entera v. A continuaci6n asigna al elemento a[O], la direcci6n de v y escribe su contenido. Cuando cada elemento de un array es un puntero a otro array, esta- mos en el caso de una doble indirecci6n 0 punteros a punteros. La caracte- ristica de poder referenciar un puntero con otro puntero, Ie da allenguaje C una gran potencia y flexibilidad para crear estructuras complejas. Un array de dos dimensiones y un array de punteros, se pueden utili· zar de forma parecida, pero no son 10 mismo. int a[lO][lOj; int *b[lOj; Este ejemplo define un array a de 100 elementos de tipo entero y un array b de 10 elementos declarados como punteros a objetos de tipo ente- rooSuponiendo que cada uno de estos objetos es un array de 10 elementos, la ocupaci6n de memoria sera de 100 elementos mas la de los 10 elementos de b. Las ventajas que tiene el array b sobre el a, es que el acceso a un ele- mento se efectua mediante una indirecci6n a traves de un puntero, en lugar
  • 226. de hacerlo mediante una multiplicacion y una suma, y que los arrays apun· tados pueden ser de longitud variable. Para especificar que una variable es un puntero a un puntero, se pro· cede de la forma siguiente: El siguiente programa multiplica los elementos de un array de dos di· mensiones por una constante y a continuacion escribe el contenido de di· cho array. Primeramente se presenta una version con arrays y despues otra con punteros. # include <stdio.h> # define F 4 / * nzimero de fi/as */ #define C 5 / * nzimero de eolumnas */ main( ) { static int tabla[F][C] { { 10, 12, 15, 17, 14 }, { 22, 20, 23, 25, 21 1 {38, 30, 34, 36, 35 }, { 45, 41, 44, 48, 49 } }; const int k int f, e; / * Multipliear eada elemento por una eonstante k */ for (f = 0; f < F; f+ +) for (e = 0; e < C; e+ +) tablaUI[e] * = k;
  • 227. / * Escribir el array */ for if = 0; f < F; f+ +) ( for (c = 0; c < c; c+ +) printft'%5d'~ tablaU][cJ); putchart n '); I I Para reescribir este programa utilizando la notaci6n de punteros en lugar de la notaci6n array, nos planteamos unicamente la cuesti6n de c6mo escribir la expresi6n tablaU][c], utilizando la notaci6n de punteros. Pues bien, pensemos que un array de dos dimensiones es un array de una di- mension, donde cada elemento es a su vez un array de una dimensi6n. En el ejemplo anterior, la direcci6n de to do el array es tabla. Este array tiene 4 elementos (filas), tabla!O] a tabla!3]. La direcci6n de cada elemento fila (array de 5 elementos) es: Si elegimos una fila, por ejemplo tabla!1], 0 en notaci6n puntero tabla + 1, interpretamos esta expresi6n como un puntero a un array de 5 elementos; esto qui ere decir que tabla +1 es un puntero a un puntero, 0 que el contenido de tabla +1, *(tabla +1), es la direcci6n del primer elemento de esa fila, tabla!1][O], 0 en notaci6n puntero *(tabla +1)+O. Las direccio- nes tabla +1 y *(tabla +1) coinciden, pero ambas expresiones tienen dife- rente significado. Por ejemplo: tabla +1+2 *(tabla+1) +2 4*(tabla+ 1)+2) se refiere a la fila tabla!3] y se refiere al elemento tabla!1][2] es el contenido del elemento tabla!1][2]. De acuerdo con 10 expuesto la versi6n con punteros del ejemplo ante- rior, presenta solamente la siguiente modificaci6n:
  • 228. / * Escribir ef array */ for if = 0; j < F; j+ +) { for (c = 0; c < c; c+ +) printj("%5d': *(*(tabfa + j) + c)); putchar(' n '); l Vamos a estudiar mediante un ejemplo, como inicializar un array de pun- teros a cadenas de caracteres. Consideremos el problema de escribir una funcion para que recibiendo un numero entero correspondiente a un mes, nos devuelva como resultado un puntero a una cadena de caracteres que corresponda al nombre de dicho meso # include <stdio.h> main( ) ( unsigned int dia, mes, anyo; char *m; char *nombre_mes(unsigned int mm); printj("Introduce jecha (dd-mm-aaaa): "); / * Los datos en fa entrada iran separados por '-' */ scanf("%u-%u-%u': &dia, &mes, &anyo); m = nombre_mes(mes); printj(" n nMes: %s n': m); l char Mombre_mes(unsigned int mm) { / * mes es un array de punteros a cadenas de caracteres */ static char *mes[ ] = { "Mes no correcto': "Enero': "Pebrero': "Marzo': "Abril': "Mayo': "Junio': "Julio':
  • 229. ':4gosto': HSeptiembre': HOctubre': HNoviembre': HDiciembre" J; return ((mm > 0 && mm < = 12) ? mes[mmj : mes[OJ); J En este ejemplo, mes[ j es un array de 13 elementos (0 a 12) que son punteros a cadenas de caracteres. Estas, como se ve, son de diferente longi- tud y todas son finalizadas automaticamente con el caracter nulo ( 0). Si en lugar de utilizar un array de punteros, hubieramos utilizado un array de dos dimensiones, el numero de columnas seria la de la cadena mas lar- ga, mas uno para el caracter nulo, con 10 que la ocupaci6n de memoria seria mayor. El siguiente ejemplo muestra un programa para clasificar cadenas de caracteres por orden alfabetico. #include <stdio.h> #include <stdlib.h> #include <string.h> #define NMAX 25 #define CMAX 81 / * mimero maximo de cadenas */ / * mimero maximo de caracteres por cadena */ main( ) / *fun cion principal */ [ char cadena[NMAXj[CMAXj; char *pcadena[NMAXj; int ncadenas; / * array de cadenas */ / * array de punteros a cadenas d / * numero de cadenas lefdas */ int LeerCadena(char cadena[ j[CMAXj, char *pcadena[ j, iut nmc); void clasijicar(char *pcadena[ j, iut nc); void escribir(char *pcadena[ j, iut nc); systemt 'els"); printj(UClasijicacion de cadenas de caracteres. n"); printj(Ulntroducir cadenas a elasijicar. Enter para salir. n "); if ((ncadenas = LeerCadena(cadena, pcadena, NMAX)) > 0) I printft'Proceso de elasijicacion. n n");
  • 230. clasijicar(pcadena, ncadenas); escribir(pcadena, ncadenas); l else printf("Cero 0 demasiadas cadenas para clasijicar n"); int LeerCadena(char cadena[ j[CMAXJ, char *pcadena[ J, int nmc) { / * nmc = numero maximo de cadenas */ int longitud, ncadenas = 0; while ((longitud = strlen(gets(cadena[ncadenasJ))) > 0) { if (ncadenas > = nmc) return (-1); / * demasiadas cadenas a ordenar */ else / * guardar el apuntador a la cadena en el array */ pcadena[ncadenas+ +J = cadena[ncadenasJ; l return (ncadenas); / * numero de cadenas lefdas */ l /************************************************************** Funci6n clasijicar **************************************************************/ / * Ordena las cadenas pcadena[OJ ... pcadena[nc - 1J ascendentemente. nc = numero de cadenas a ordenar */ void clasijicar(char *pcadena[ J, int nc) { char ~W(; / *puntero auxiliar */ int i, s = 1;
  • 231. while ((s = 1) && (--nc > 0)) { s = 0; I. no permutaci6n .1 for (i = 1; i < = nc; i+ +) if (strcmp(pcadena[i-1J, pcadena[i]) > 0) { aux = pcadena[i-1J; pcadena[i-1J = pcadena[iJ; pcadena[iJ = aux; s = 1; I. permutaci6n .1 } } } 1**•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• Funci6n escribir **.**••••••••••••••••••••••••••••••••••••••••••••••••••••••• **1 void escribir(char .pcadena[ J, iot nc) { 1* nc = mimero de cadenas a escribir .1 while (--nc > = 0) printj(H%s n': .pcadena + +); El programa utiliza arrays de caracteres y punteros a cadenas de ca- racteres. La clasificacion y, por consiguiente, la escritura del resultado, se realiza sobre el array de punteros, ya que resulta mas facH. Existen dos metodos fundamentales que C utiliza para almacenar infor- macion en la memoria. El primero utiliza variables globales y locales. En el caso de variables globales, el espacio es fijado y utilizado a 10 largo de toda la ejecucion del programa (~ATA); yen el caso de variables loca- les, la asignacion se hace a traves del stack.
  • 232. El segundo metodo utiliza funciones predefinidas en C, como mal/ocr ) y free(). Como es logico, estas funciones utilizan el area de memoria libre (Heap), para .realizar las asignaciones de memoria. ROM BIOS Extensiones BASIC y BIOS Memoria para video Programa DOS Heap Stack _DATA _TEXT La asignacion dimimiCa de memoria consiste en asignar la cantidad de memoria necesaria para almacenar un objeto durante la ejecucion del programa, en vez de hacerlo en el momento de la compilacion del mismo. Cuando se asigna memoria para un objeto de un tipo cualquiera, se de- vuelve un puntero a la zona de memoria asignada.
  • 233. La funci6n mal/oc devuelve un puntero que referencia el espacio asig- nado, a un objeto de un tipo no especificado. Si hay insuficiente espacio de memoria 0 si t es 0, la funci6n retorna un puntero nulo (NULL). El espacio puede ser asignado a cualquier tipo de objeto. Para realizar ----""laconversi6n al tipo deseado, utilizar la notaci6n cast sobre el valor devuelto. El siguiente programa asigna espacio para cadenas de caracteres. Los punteros a cada cadena son guardados en un array de punteros. # include <stdio.h> # include <stdlib.h> # include <string.h> # define NML 100 / * numero maximo de lfneas */ main( ) ( char *plinea[NML]; / * array de punteros alas cadenas */ char *p, linea[81]; iot i, longitud, nlineas = 0; system (Hcls"); printf(Hlntroducir cadenas de caracteres. n"); printf(HFinalizar con Enter. n n"); while ((longitud = strlen(gets(linea))) > 0 && nlineas < NML) ( / *Asignar espacio para una cadena de caracteres */ p = (char *)mal/oc(longitud +1); if (p = = NULL) ( printft'Insujiciente espacio de memoria disponible n"); exit(l); * terminar el proceso */ J
  • 234. else [ / * copiar la cadena en el espacio de memoria asignado */ strcpy(p, linea); / * guardar el puntero a la cadena en el array */ plinea[nlineas+ +J = p; } } / * Escribir las cadenas almacenadas */ system("cls "); printj("Lfneas almacenadas: n n "); for (i = 0; i < nlineas; i+ +) printj("%s n': plinea[i]); La sentencia: p = (char *)mal/oc(longitud +1); asigna un espacio de memoria de longitud +1 bytes, para una cadena de caracteres. EI espacio de memoria es referenciado por e1 puntero p, el cual ha sido declarado como un puntero a una cadena de caracteres. Recordar que la funci6n srtlen() no contabiliza el canicter nulo de terminaci6n; de ahi la expresi6n longitud +1. Utilizando la funci6n mal/ocr ) y otras que se presentan a continuaci6n, es posible definir arrays en tiempo de ejecuci6n, denominados tambien arrays dimimicos. scanj("O/Od': &n_elementos); a = (iot *)mal/oc(n_elementos * sizeof(iot)); for (i = 0; i < n_elementos; i+ +) scan!("O/Od': &a[i]);
  • 235. Esta funci6n asigna espacio de memoria para un array de n elementos, de longitud t bytes cada uno de ellos. Cada elemento es inicializado a o. La funci6n calloc( ) devuelve un puntero al espacio asignado. Este es- pacio puede ser asignado a un array de cualquier tipo. Para ello, utilizar la notaci6n cast sobre el valor devuelto. Si hay insuficiente espacio de memoria, 0 si not son 0, la funci6n retorna un puntero nulo (NULL). EI espacio de memoria requerido debe ser inferior a 64K. EI siguiente programa asigna espacio para un array de N elementos de tipo entero. # include <stdio.h> # include <stdlib.h > const int N = 100; / * numero maximo de elementos para el array :~/ main( ) [ int i = 0; / * Asignar espacio para N enteros */ !ista = (int *)calloc(N, sizeof(int));
  • 236. if (lista = = NULL) printj(Hlnsuficiente espacio de memoria disponible n"); else printf(HEspacio de memoria asignado para N enteros n n"); lista[9] = 100; ~ while (i+ + < N) printj(H%8d': *lista+ +); EI puntero devuelto por la funci6n cal/oc( ) es convertido mediante la notaci6n cast (int *), para que apunte a objetos de tipo entero y almace- nado en la variable lista. Esta funci6n cambia el tamafio de un bloque de memoria previamente asig- nado. EI argumento p es un puntero que apunta al comienzo del bloque. Si p es NULL, esta funci6n se comporta igual que mal/ocr ) y asigna un nuevo bloque de t bytes. Si p no es nulo, entonces tiene que ser un puntero devuelto por las funciones mal/ocr ), cal/oc( ) 0 por la propia funci6n rea- l/oc( ). EI bloque ha po dido, incluso, ser liberado por la funci6n free(). EI argumento t, da el nuevo tamafio del bloque en bytes. EI contenido del bloque no cambia en el espacio conservado. La funci6n real/ocr) devuelve un puntero al espacio asignado. EI blo- que puede ser movido al modificar el tamafio, esto quiere decir que p pue· de cambiar. Este espacio puede ser asignado a un objeto de cualquier tipo. Para ello, utilizar la notaci6n cast sobre el valor devuelto. Si hay insuficiente espacio de memoria 0 si t es 0, la funci6n retorna un puntero nulo (NULL). Cuando esto ocurre, el bloque original es liberado.
  • 237. Esta funci6n libera un bloque de memoria asignado por las funciones ma- lloc(), calloc( ) 0 realloc( ). Un puntero nulo es ignorado. EI siguiente programa muestra c6mo realizar una reasignaci6n de me- moria; y pone de manifiesto que despues de una reasignaci6n, la informa- cionno varia en el espacio de memoria conservado. Por ultimo, el bloque de memoria es liberado. # include <stdio.h > # include <stdlib.h> # include <string.h> const int BYTES = 40; main() I / * asignar espacio para una cadena de caracteres */ p = malloc(BYTES * sizeof(char)); / * asignar cadena de caracteres */ strcpy(p, "abcdef 0"); / * reasignar el bloque para contener mas caracteres */ if (p != NULL) p = realloc(p, BYTES *2 * sizeof(char));
  • 238. if (p != NULL) ( printf(HBloque reasignado n"); / * Escribir la cadena original */ printft'%s n': p); / * Liberar el espacio de memoria */ jree(p); printf(H nEI bloque ha sido liberado n"); } else ( printf(HLa reasignaci6n no ha sido posible n"); printf(HEI espacio ocupado por el bloque ha sido liberado"); } } Esta funcion asigna espacio de memoria para un array de n elementos, de longitud t bytes cada uno de ellos. EI espacio total de memoria puede ser mas grande que 64K (array huge). La funcion halloc( ), devuelve un puntero que referencia el espacio asignado. EI espacio puede ser asignado a cualquier tipo de objeto. Para ello rea· lizaremos una conversion explicita sobre el valor retornado, utilizando la notacion cast. Esta funcion libera un bloque de memoria asignado por la funcion haUoen #include <malloc.h> void hfree(void _huge *p);
  • 239. Lospunteros a estructuras se declaran igual que los punteros a otros tipos de datos. C utiliza el operador - > para referirse a un miembro de una es- tructura apuntada por un puntero. EI siguiente ejemplo declara un puntero hoy a una estructura, asigna un valor a cada miembro de la misma y, apoyandose en una fundon, escri- be su contenido. # include < stdio.h > # include < stdlib.h > structjecha / * declaraci6n del tipo estructura jecha */ ! unsigned int dd,o unsigned int mm,o unsigned int aa,o ]; void escribir(struct jecha 4); main() ! struct jecha *hoy,o/ * hoy es un puntero a una estructura */ / * asignaci6n de memoria para la estructura */ hoy = (struct jecha *)malloc(sizeof(struct jecha)),o printj("Introducir jecha (dd-mm-aa): "),o scanf("%u- %u- %u': &hoy- >dd, &hoy- >mm, &hoy- >aa); escribir(hoy),o ] void escribir(struct jecha 4) ! printf("Dia %u del mes %u del ano %u n': j- >dd, j- >mm, j- >aa); ]
  • 240. Observar que el tipo struct jecha va definido fuera de toda funci6n, para poder utilizarlo en cualquier parte y que mediante la fund6n mal/oc() asignamos memoria para un objeto de tipo struct jecha. Los punteros a uniones se manipulan exactamente igual que los pun- teros a estructuras. Una declaraci6n compleja es un identificador calificado por mas de un ope- rador (array: [ ], puntero: *, 0 funci6n: ( ) ). Se pueden aplicar varias com.' binaciones con estos operadores sobre un identificador; sin embargo, un array no puede contener funciones en sus elementos y una fund6n no pu~- de devolver como resultado un array 0 una funci6n. Para interpretar estas declaradones, hay que saber que los corchetes y parentesis (operadores a la derecha del identificador) tienen prioridad sobre los asteriscos (operadores a la izquierda del identificador). Los pa- rentesis y corchetes tienen la misma prioridad y se evaluan de izquierda a derecha. Como ultimo paso se aplica el tipo especificado. Utilizando pa· rentesis, podemos cambiar el orden de prioridades. Las expresiones entre parentesis se evaluan primero, de mas internas a mas externas. Una simple forma de interpretar declaraciones complejas es leerlas desde dentro hacia afuera, de acuerdo con los siguientes pasos: 1. Comenzar con el identificador y mirar si hacia la derecha, hay cor- chetes 0 parentesis. 2. Interpretar esos corchetes 0 parentesis y mirar si hacia la izquier- da hay asteriscos. 3. Dentro de cada nivel de parentesis, de mas internos a mas exter- nos, aplicar las reglas 1 y 2. char *( *( *var) ( )) [10J !I. !I. !I.!I. !I. !I. !I.
  • 241. En este ejemplo se han numerado los pasos en el orden de interpreta- cion, que es como sigue: Nota: Para una informacion avanzada sobre punteros ver el capitulo titu- lado "Manejo de la memoria".
  • 242. Una funcion es una coleccion independiente de declaraciones y sentencias, generalmente enfocadas a realizar una tarea especifica. Todo programa C consta al menos de una funcion, la funcion main( ). Ademas de esta, pue- dehaber otras funciones cuya finalidad es, fundamentalmente, descompo- ner el problema general en subproblemas mas faciles de resolver y de man- tener. La ejecucion de un programa comienza por la funcion main( ). Cuando se llama a una funcion, el control se pasa a la misma para su ejecucion; y cuando finaliza, el control es devuelto de nuevo al modulo que llamo, para continuar con la ejecucion del mismo a partir de la sen- tencia que efectuo la llamada. main( ) [ func1(); func1(); J func1( ) [ func2( ) [
  • 243. La definici6n de una fund6n consta de la cabecera de funcion y del cuero po de la funcion. La sintaxis correspondiente es: [clase] [tipo] nombre([pardmetros-jormales]J { [declaraciones] sentencias; define el ambito de la funci6n, esto es, desde donde puede ser Hamada. La clase puede ser: extern 0 static. Una fund6n static es visible solamente en el fichero fuente en el cual est a definida; y una fund6n extern es visible para todos 10s ficheros fuente que componen el programa. Por defecto, la clase de una funci6n es extern. indica el tipo del valor devuelto por la funci6n. Puede ser cualquier tipo fundamental 0 tipo definido por el usuario. Por defecto, el tipo es into Una funci6n no puede retornar un array 0 fund6n, pero si puede retornar un puntero a un array 0 a una fund6n. es un identificador que indica el nombre de la funci6n. Si el nombre va precedido por el operador asterisco (*), el va- lor devuelto por la funci6n es un puntero. panimetros formales componen la lista de argumentos de la funci6n. Esta lista consta de un conjunto de variables con sus tipos, sepa- radas por comas y encerradas entre parentesis. Los panime- tros formales son variables que reciben los valores pasados en la Hamada a la funci6n. La sintaxis es la siguiente: a parte del alrnacenamiento por defecto (auto), esta es la unica clase de almacenamiento permitida para un parame-
  • 244. tro formal. Una variable register se coloca en los registros de la UCP, 10 cual da lugar a programas mas cortos y rapidos. indica el tipo del argumento, el cual puede ser un tipo fun- damental, 0 un tipo definido por el usuario. Por defecto es into Sino se pasan argumentos a /a /Line.ion, /a usta de pa.afmetros JOrma- les puede ser sustituida por la palabra clave void Sies necesario, el compilador ejecuta las conversiones aritmeticas usua- les sabre cada parametro formal y sobre cada argumento actual. Este ejemplo define una funci6n Hamada suma, que acepta dos valo- s enteros y retorna un valor entero. Este ejemplo define una funci6n Hamada calculo, sin argumentos, la cual retorna un valor real (double). Esteejemplo define una funci6n Hamadajx, que acepta dos argumen- s: a de tipo entero (int) y de clase de almacenamiento register y p que un puntero a un valor de tipo char. EI resultado devuelto por la funci6n un valor de tipo entero (int).
  • 245. Este ejemplo define una funci6n Hamada suma, que acepta dos argu- mentos, datol y dato2, de tipo entero (long). El valor retornado por la fun- ~ ci6n es un puntero a un valor de tipo entero (long). Este ejemplo declara un puntero denominado suma, a una funci6n que acepta dos argumentos, datol y dato2, de tipo entero (long) y que retorna un valor tambien de tipo entero (long). Este ejemplo define una funci6n Hamada dibujar, sin argumentos, la cual no retorna un valor. Este ejemplo define una funci6n multiplicar, que acepta dos argumentos reales, datol y dato2, y retorna un valor que es un puntero a un array de cinco elementos de tipo real. Este ejemplo define una funci6n puntero, que acepta un argumento p, declarado como un puntero a un objeto de tipo no especificado. La fun- ci6n retorna un valor entero. Este ejemplo define una funci6n jcalculo, que acepta dos argumen- tos: n de tipo entero y pj que es un puntero a una funci6n que acepta un argumento x entero y devuelve como resultado un valor de tipo long. La funci6n jcalculo no retorna un valor. La definici6n de una funci6n puede hacerse tambien, utilizando el for- mato antiguo.
  • 246. int suma(datal, data2) int datal, data2; ( [dec/aracianes] sentencias; l int suma(int datal, int data2) { [dec/aracianes] sentencias; l El cuerpo de una funcion esta formado por una senten cia compuesta que contienesentendas que definen 10 que hace la fundon. Tambien puede con- tener declaraciones de variables utilizadas en dichas sentendas. Estas va- riables, por defecto, son locales a la fundon. Hemos visto que cada funcion puede devolver un valor cuyo tipo se indica enla cabecera de funcion. Este valor es devuelto a la sentenda de Hamada ala fundon, por medio de la sentenda return, cuya sintaxis es la siguiente: Si la sentenda return no se especifica 0 se especifica sin contener una expresi6n, la fundon no devuelve un valor.
  • 247. variable especifica la variable donde va a ser almacenado el valor de· vuelto por la funcion. Notar que la llamada puede prescindir del valor devuelto por la funcion. expresion especifica una direccion que referencia a una funcion. Puede ser, una expresion que es evaluada a una direccion de una fun· cion, 0 simplemente un identificador que se corresponde con el nombre de la funcion llamada. Esto significa que una fun· cion puede ser llamada a traves de un puntero a una funci6n. panimetros-actuales son una lista de expresiones separadas par comas. Las expresiones son evaluadas y convertidas utilizando las conver- siones aritmeticas usuales. Los valores resultantes son pasados a la funcion y asignados a sus correspondientes panimetros fOf' males. El numero de expresiones en la lista, debe ser igual al numero de parametros formales, a no ser que se especifique un numero variable de argumentos. Este ejemplo llama a la funcion suma( ) y Ie pas a los valores de a y b * 2. Si la funcion devuelve un valor, este no se tiene en cuenta. Este ejemplo llama a la funcion multiplicar( ) y Ie pas a los valores de a y b. La funcion devuelve un valor que es almacenado en la variable r.
  • 248. Este ejemplo llama a la funci6n mayor( ) y Ie pasa los valores de v[i} y de v[i+1] (elementos del array v). La funci6n devuelve un valor que es almacenado en la variable n. Cuando se ejecuta una llamada a una funci6n, el control es pasado a la misma para su ejecuci6n. La ejecuci6n de la funci6n finaliza cuando se ejecuta una sentencia return 0 cuando se llega al final del cuerpo de la funci6n. En este instante, el control es devuelto al punta de llamada para continuar la ejecuci6n del programa. La declaraci6n de una funci6n, denominada tambien funcion prototipo, permite conocer el nombre, el tipo del resultado, los tipos de los panime- tros formales y opcionalmente sus nombres. No define el cuerpo de la fun- cion.Esta informaci6n permite al compilador chequear los tipos de los pa- nimetros actuales por cada llamada a la funci6n. Una funci6n no puede ser Hamada si previamente no esta definida 0 declarada. A esta regIa hay una excepci6n que veremos a continuaci6n. static double escribir(double x, int y) / *funci6n escribir */ [ return x +y *1.3; J main( ) / *funci6n principal */ [ double r, a = 3.14; int b = 5; r = escribir(a, b); / * llamada a la funci6n escribir */ printf("%g n': r); J Observar que la definici6n de la funci6n es anterior a la llamada. Si estono sucede asi, entonces es necesario declarar la funci6n antes de que
  • 249. Una funcion prototipo tiene la misma sintaxis que la definicion deuna funcion, excepto que esta termina con un punta y coma. ..A~~.)lamada. La dec1aracion de una fundon puede ocurrir a nivel interno ':(j" a nivel externo. '1,::.-.......... Ajxp;tRl,~,.",. ",j.ftti-.,. ,,:.'/'#f#clude <stdio.h> . .,.' _...•~. ',:." !l{ static double escribir(double x, int y); / *funci6n prototipo */ main() / *funci6n principal */ ( double r, a = 3.14; int b = 5; r = escribir(a, b); / * llamada a la funci6n escribir */ printj(H%g n': r); } static double escribir(double x, int y) / *funci6n escribir */ { return x +y *1.3; } Una fundon prototipo recibe tambien el nombre de declaracion fOf· ward; esto es, una dec1aracion que precede a la definicion de la fundon. Si una Hamada a una fundon precede a su definicion, y no existeuna declaracion forward de la misma, implidtamente esta se construye con tipo de resultado into main( ) ( int r, b = 5; r = escribir(b); / * llamada a la funci6n */
  • 250. / * Este programa busca un ntimero en una !ista * e injorma de su posicion */ #include <stdio.h> # include <std!ib.h > # include <ctype.h > #dejine N 40 const int NO = 0; main( ) ( float !ista[N]; float numero,o int i = 0; int encontrado; / * !ista de mlmeros */ / * valor a buscar */ / * ntimero de valores lefdos */ / * 0 = no encontrado * otro valor = posicion del n° en la !ista */ system ("cls "); printj(Hlntroducir !ista de ntimeros. n"); printj("Fina!izar con A Z. n n"); while (scanj("%f': &!ista[i+ +J) != EOF && i < N) clearerr(stdin); / * desactivar el indicador EOF */ / * Busqueda de un numero cualquiera en la !ista */ do ( system("cls"); printj("Indique el ntimero que desea buscar "); scanj("%f': &numero); encontrado = BusquedaSecuencial(!ista, i, numero); if (!encontrado) printj(" nEste n° no estci en la !ista n "); else printj(" nEste nO estci en la posicion %d n': encontrado);
  • 251. printft' n n;, Desea continuar ? sin "),o do resp = getch( ),o while (tolower(resp)!= 's' && tolower(resp) != 'n'),o l while (resp = = 's'),o l 1************************************************************** Busqueda Secuencial **************************************************************1 int BusquedaSecuencial(f1oat lista[N], int i, float numero) [ int encontrado = NO, k 0; while (!encontrado && k < i) { if (lista[k+ +] = = numero) encontrado = k,o l return encontrado,o l Observar que la entrada de datos se realiza mediante la repetici6n de la ejecuci6n de una sentencia scanf(). Esta entrada finaliza cuando se pul- sen las teclas etr) +Z (F6), 10 que hace que se active el indicador EOF de fin de fichera. Este indicador queda activado mientras no se desactive ex- plicitamente, 10 que da lugar a que las siguientes sentencias scanf( ) no sean ejecutadas. Pues bien, para desactivar el indicador EOF es necesario eje- cutar la funci6n clearerror(stdin). 1. Almacene en una array, el numera de matricula, apellidos, nom- bre y direcci6n de cada uno de 10s alumnos de un determinado curso.
  • 252. 2. Busque la ficha carrespandiente a un alumna, par su numero de matricula. # include <stdio.h> # include < stdlib.h > # include < ctype.h > # include <string.h> int leer(int); void buscar(char *, int, int); char resp; main( ) { char m[30}; int opcion, n = 0; while (1) / * bucle infinito; se sale con break */ { do ( system ("cls "); printj(" n tl. Entrada de datos de alumnos n "); printjt' n t2. Bzisqueda por nro. de matrfcula n"); printj(" n t3. Bzisqueda por apellidos n "); printj(" n t4. Fin n "); printj(" n nTeclee l~ opcion deseada "); scanf("%d': &opcion); J while (opcion < 1 II opcion > 4); fflush(stdin); / * limpiar el buffer de la errirada estdndar */ if (opcion 1= 4) { switch (opcion) {
  • 253. case 1: I * entrada de datos de alumnos *1 resp = 's'; while (tolower(resp) = = 's') [ leer(n+ +); printj(ff n nj, Mas datos a introducir ? sin "); resp = getche( ); } break; case 2: 1* btisqueda por ntimero de matrfcula *1 systemt 'cis' '); printf(ffNtimero de matrfcula "); gets(m); buscar(m, n, opcion); break; case 3: 1* Btisqueda por apellidos *1 system(ffcls"); printj(ffApellidos.......... "); gets(m); buscar(m, n, opcion); break; } } else break; 1************************************************************** Funci6n para leer los datos correspondientes a un alumno **************************************************************1 #deJineN 20 1* ntimero maximo de alumnos *1 struct Jicha [ char matricula[10]; char apellidos[30]; char nombre[20]; char direccion[30]; } lista[N];
  • 254. int leer(int n) { do ( system("c!s "); printf("Alumno mimero %d n n': n + l),~ printj("Numero de matrfcula "); gets(lista[n};matricula); printj("Apellidos................ "); gets(lista[h};apellidos); printf("Nombre........... . "); gets(lista[hf.nombre); printf("Direccion "); gets(lista[n}.direccion); printj(" n nz Datos correctos ? sin "); resp = getche( ); l while (tolower(resp) /= 's'); J 1************************************************************** Funcion para buscar si existe 0 no un dato **************************************************************/ const int NO = 0; const int S1 = 1; void buscar(char x[30}, int alumnos, int opcion/ ( int existe = NO, i = 0; switch (opcion,) { case 2: 1* busqueda por numero de matrfcula *1 while (/existe && i < alumnos) if (strcmp(lista[i + +}.matricula, x) = = 0) existe = S1; break; .:ase 3: 1* Busqueda por apellidos *1 while (/existe && i < alumnos) if (strcmp(lista[i+ +j.apellidos, x) - - 0) existe = S1; break;
  • 255. if (existe) printj(" n%s n%s %s n%s n': lista[i-1].matricula, lista[i-l].apellidos, lista[i-1].nombre, lista[i-1].direccion); clSe printjt' n%s no existe': x); printj(" n nPulse una tecla para continuar "); resp = getch( ); ) Es necesario tener en cuenta que, despues de ejecutarse la sentencia scanf("%d': &opcion), en el buffer de la entrada estandar stdin queda el caracter ' n' (separador para scanf( )). Como es un caracter valida para una funcion como gets( ), es necesario limpiar este buffer, antes de que se ejecute una funcion como esta. Esta operacion se realiza mediante la funcion jjlush(stdin). Realizar un programa que lea una fecha comprendida entre los afios 1980y 2100, verifique si es valida y la imprima # include <stdio.h > # include <stdlib.h> main( ) { unsigned int dia, mes, anno, s; do ( system("cls "); s=l; I*s 1* s = 1, indica jecha valida *1 0, indica jecha no valida *1
  • 256. printj(Hlntroducir la jeeha: n"); printj(H. dfa: "); seanj(H%u': &dia); printj(H mes: "); seanj(H%u': &mes); printj(H ano: "); seanj(H%u': &anno); s = jeeha(dia, mes, anno); --I while (s = = 0); } 1************************************************************** Verifiear e imprimir jeeha **************************************************************( int jeeha(int dd, int mm, int aa) ( unsigned int diasmes; int c1, e2; 1* Caleular los dfas del mes *1 witch (mm) ( case 1: case 3: case 5: case 7: case 8: case 10: case 12: diasmes = 31; break; case 4: case 6: case 9: case 11: diasmes = 30; break; case 2: 1* eomprobar si el ano es bisiesto *1 if ((aa % 4 = = 0) && (aa % 100 1= 0) II (aa % 400 0)) diasmes = 29; else diasmes = 28; break; default: putehart 7'); 1* earaeter bell *1 return (0); 1* jeeha no valida *1 } c1 = dd > = 1 && dd < = diasmes; e2 = aa > = 1980 && aa < = 2100; if (c1 && e2) ( printj(H ndfa-mes-ano: %U-%U-%IJ n': dd, mm, aa);
  • 257. return (1); J else [ putchar( 7'); return (0); J J / * caracter bell */ / *fecha no valida */ Hay dos formas de pasar los parametros actuales a sus correspondientes parametros formales, cuando se efectua la llamada a una funci6n: 1. Por valor. 2. Por referencia. Pasar parametros por valor, significa copiar los parametros actuales en sus correspondientes parametros formales, operaci6n que se hace auto- m<iticamentecuando se llama a una funci6n, con 10 cual no se modifican los parametros actuales. Pasar parametros por referencia, significa que 10 transferido no son losvalores, sino las direcciones de las variables que conti enen esos valores, can 10 cual los parametros actuales pueden verse modificados. Cuando se llama a una funci6n, los argumentos especificados en la Hamada son pasados por valor; excepto los arrays que se pasan por refe- rencia, ya el nombre del array es un puntero a dicho array. Utilizando la forma de pasar parametros por valor, pueden ser trans- feridas constantes, variables y expresiones; y utilizando la forma de pasar parametros por referencia, solamente se permite transferir las direcciones de variables de cualquier tipo, arrays y funciones. Para pasar una variable por referencia, se pasa la direcci6n del para- metro actual a su correspondiente parametro formal, el cual tiene que ser un puntero. Para ello, utilizar el operador & antes del nombre de la varia-
  • 258. ble. Para pasar la direcci6n de un array 0 de una funci6n, no es necesario esteoperador antes del nombre del array 0 del nombre de la funci6n. main( ) ( int v = 5, suma; sumar(4, v, v*2-1, &suma); / * llamada a fa funci6n */ printf(H%d n': suma); l int sumar(int a, int b, int c, int *-5) ( b += 2; *-5=a+b+c; La Hamada a la funci6n sumar( ), pasa a esta funci6n los panimetros 4, v y v*2-1 por valor y el panimetro sum a por referencia. Cualquier cambio que sufra el contenido del panimetro formal s, su- cede tambien en su correspondiente panimetro actual suma. En cambia, la variable v, no se ve modificada, a pesar de haber variado b, ya que ha sido pasada por valor. Para pasar todos los elementos de un array a una funci6n, se pone enla lista de panimetros actuales el nombre del array, que es la direcci6n de co- mienzo del array; y en la lista de panimetros formales, el nombre del array seguido de sus dimensiones. De estas, como ya dijimos, se puede omitir la primera, pero no los corchetes que la contienen.
  • 259. #include <-sttiio.h> #include <stdlib.h > #dejine FILAS 12 #dejine COLS 4 void ElementosNulos(f1oat !][COLS], const int, const int, int *); main( ) I int cont; system((cls "); ElementosNulos(datos, FILAS, COLS, &cont); printj((Hay %d elementos nulos n': cont); l void ElementosNulos(f1oat mat! ][COLS], const int ji/as, const int cols, int *conta) 1* Da como resultado el ntimero de.elementos igual * a cero, en un array de dos dimensiones. */ int 1, c; ~onta = 0; mat[1l][3] = 100; for if = 0;j < ji/as; j + +) for (c = 0; c < cols; c+ +) if (matff][c] = = 0) + + *conta;
  • 260. El panimetro formal mat! ][COLS], puede especificarse tambien de la forma: mat!FILAS][COLSj. La variable cont se pasa por referencia. Cualquier cambio que sufra ~onta, 10 sufflra1ambien su correspondiente panimetro actual cont. Igual- mente sucedeni con los elementos del array. Otra forma de pasar un array es utilizando la notaci6n de punteros. Aplicando esta notaci6n al ejemplo anterior, obtendriamos: void ElementosNulos(f1oat **mat, const int Ji/as, const int cols, int *conta) Realizar un programa que lea lineas de texto y nos de como resultado la linea mas larga. Cada linea finalizara con un retorno de carro y el texto con EOP. # include <stdio.h > # include <stdlib.h> void leer_linea(char linea! ], int *long_linea); void copiar(char linea! ], char linea_maxI ], int long_max);
  • 261. main( ) { char linea[CARS-LINEAJ, linea_max[CARS-LINEAJ; iot long_linea = 0, long-"lax = 0; systemt<c/s"); printf(Hlntroducir lfneas de texto. Finalizar con AZ. n n"); while ((linea[OJ = getchar( )) != EOF) { long_linea = 1; leer_linea(line~ &long_lineat if (long_linea > long-"lax) { long_max = long_linea; copiar(linea, linea_max, long_max); } } if (long_max > 0) { printf(H nLfnea de texto mds larga: n"); printf(H n%s n': linea_max); } } 1************************************************************** Funci6n leer /(nea **************************************************************/ void leer_linea(char linear J, iot *long_linea) { while ((lineablong_lineaJ = getchar( )) != < n' && *long_linea < CARS-LINEA-1) + + *long_linea; / * la cadena se jinaliza con el cardcter nulo t 0'). * convenio utilizado por C */ linear*long_lineaJ = < 0'; J
  • 262. /************************************************************** Funcion copiar **************************************************************/ / * Guardar en linea_max la lfnea mas grande en curso */ void copiar(char linea! ], char linea_maxI ], int long_max) [ for (i =0; i < = long_max; i+ +) linea-"lax!i] = linea!i]; La fundon leer_linea(), lee una linea de texto y calcula su longitud. Si resulta que esta linea es la mas larga de las leidas hasta ahora, la fundon copiar( ) la guarda en el array linea_max, para al final escribirla. Un puntero igual que otros tipos de variables puede ser pasado por valor o por referenda. # include < stdio.h > # include <string.h > struct p [ char c!20]; int n; }; typedef struct p pareja;
  • 263. main( ) [ pareja *0 = NULL, *b = NULL; juncion.-X(a, &b); printj("pareja apuntada por a: %-20s %5d n': a->c, a->n); printf("pareja apuntada por b: %-20s %5d n': b- >c, b- >n); ) void juncion.-X(pareja *p, pareja **lJ) [ P = (pareja *)malloc(sizeof(pareja)); strcpy(p- >c, "cadena a"), p- >n = 1; *q = (pareja *)malloc(sizeof(pareja)); strcpy((*lJ)->c, "cadena b"), (*lJ)->n = 2; ) pareja apuntada por a: (null) pareja apuntada por b: cadena b 26956 2 En el ejemplo anterior, tratamos de asignar un valor alas estructuras apuntadas por a y por b, utilizando una funci6n. Esta fund6n tiene dos parametros formales, correspondientes a los parametros actuales a y b, los cuales son: p puntero a un objeto de tipo pareja y q que es un puntero a un puntero a un objeto de tipo pareja. En base a 10 expuesto, a es pasado por valor y b es pasado por referenda. La funci6njuncion.-X(), primeramente asigna memoria para un ob- jeto de tipo pareja y 10 deja apuntado por p. Este objeto no es visible en main(), ya que en la llamada a la fund6n 10 que pasamos a p, fue el valor de a. Ahora p, ha tornado un nuevo valor que no es reflejado en a. A continuad6n se asigna memoria para otro objeto de tipo pareja, el cual queda apuntado por q. Este objeto es visible en main( ), ya que q referenda a b, por 10 que todo cambio efectuado sobre *q, se vera tambien reflejado sobre b.
  • 264. Otro detalle que cabe resaltar, es la utilizaci6n de la funci6n strepy( ) para copiar una cadena en otra. Seguramente algunos intentarian resolver esta cuesti6n utilizando, por ejemplo, la sentencia: char a[20], b[20] = "abed"; a = b; / * error */ El error que se obtiene es debido a que a es una constante, por 10 que su valor no es modificable. En cambio si realizamos las declaraciones: char ~, *b = "abed"; I = b; / * eorreeto */ el resultado es correcto, ya que a es una variable. En este caso estamos asig- nando e1valor de la variable puntero b, a: la variable puntero a. Ahara a y b apuntan a la cadena "abed': La funci6n strepy( ) copia contenidos, no direcciones. Cuando se ejecuta un programa C, la primera funci6n que se llama para su ejecuci6n es main( ). En general, el formate para esta funci6n es: En muchas ocasiones cuando invocamos un programa desde e1sistema ope- rativo, necesitamos escribir uno 0 mas argumentos a continuaci6n del nom· bre del programa, separados por blancos. En este ejemplo, progQl es el program a que ejecutamos y -n es el argumen- to a procesar inmediatamente, bajo este programa. La cadena "-n" recibe el nombre de argumento en linea de 6rdenes.
  • 265. argc es un entero que indica el numero de argumentos en la linea de ordenes. argv es un array de punteros a cadenas de caracteres. Cada elemento del array apunta a un argumento, de manera que: argv[O]contie- ne el nombre del programa, argv[1]el primer argumento de la li- nea de ordenes, argv[2] el segundo argumento, etc. Los argumentos de main( ) se han denominado arge y argv por conve- nio.Esto quiere decir que podriamos utilizar otros identificadores para nom- brarlos. El siguiente ejemplo muestra como acceder al contenido de estos pa- nimetros desde un programa. El programa que se presenta a continuacion, simplemente escribe los argumentos pasados desde la linea de ordenes a la funcion main( ). main(int arge, char *argv[J) [ int i; if (arge < 2) / * ehequear el mlmero de argumentos */ printft'No hay argumentos para %s n': *argv); else [ printf("Nombre del programa: %s n n': *argv); argv+ +, arge--; / * eliminar el argv[O] */ printj("Nl1mero de argumentos pasados: %d n n': arge); for (i = 1; i < = arge; i+ +) printj(" argumento %d: %s n': i, ~rgv+ +); Orden de ejecucion: C: >prog01 uno dos tres
  • 266. argumento 1: uno argumento 2: dos argumento 3: tres Por ser argv un array de punteros a cadenas, existen varias formasde acceder al contenido de sus elementos. Estas son: EI numero de parametros de una funcion puede ser variable. La notaci' empleada. para especificar esta caracteristica es la elipsis; tres puntos (... Por ejemplo: Este ejemplo declara una funcion que no retorna un valor, y que p de tomar un numero variable de argumentos de tipo no especificado, Si algunos argumentos, pero no todos, son conocidos, estos deben listados primero. En este caso, el numero de argumentos especificados la Hamada, debe ser como minima igual al numero de parametros for les que aparecen en la definicion de la funcion.
  • 267. Este ejemplo declara una funcion llamada buscar, que acepta al me- nos un argumento, ptr, declarado como un puntero a un valor de tipo char. El valor retornado por la funcion es un puntero a un valor de tipo char. Si la funcion espera un numero de argumentos variable y de tipos no conocidos, tendremos que utilizar funciones con un mimero de argumen- tos variable. Todos los argumentos especificados en la llamada son colocados en la pila (stack). El recuperar de la pila estos argumentos es responsabilidad del programador. Para ello, y con el fin de tener un punta de referencia, se debe especificar al menos un argumento; este no puede ser de tipo void ni de clase de almacenamiento register. Cuando un argumento no es declarado, el com pi lad or no tiene la in- formacion necesaria para ejecutar el chequeo y conversion sobre dicho pa- nimetro, dejando esta problematica al programador. Con el fin de salvar los problemas anteriores y hacer mas sencilla la manipulacion de funciones con un numero variable de argumentos, hay construidas un conjunto de macros estandar, las cuales se localizan en el fichero stdarg.h. Estas macros son las siguientes: Se utiliza para declarar la variable ap (puntero a un char), que utiliza- remos para direccionar sucesivamente, los argumentos colocados en la pBa despues de la llamada a la funcion. Coloca en la variable ap la direccion del primer argumento no nom- brado. Esta direccion se calcula a partir de v, que es el nombre del ultimo argumento especificado.
  • 268. Calcula la direcci6n del siguiente argumento de tipo t no nombrado, y da como resultado el valor (no la direcci6n, debido a [-1]) del argumento colocado en la direcci6n ap anterior (sub in dice -1). Inicializa la variable ap al valor NULL. Cuando necesitamos recupe- rar mas de una vez los argumentos, es necesario Hamar previamente a esta funci6n. Supongamos que queremos escribir una funci6n error( ) que toma un numero entero de argumentos, los cuales indican la gravedad del error por un numero arbitrario de cadenas de caracteres. La idea es formar el men- saje de error a partir de las palabras pasadas como argumentos en linea de 6rdenes. # include <stdio.h> # include <stdlib.h > # include <stdarg.h > void error(int, ...); main(int argc, char *l1rgv[ J) ( switch (argc) ( case 1: error(O, argv[O], 0); break; case 2: error(O, argv[O], argv[1], 0); break; default: if?afargc-l, aro:s, 10);
  • 269. void error(int n, ...) ( va_list p; va-start(p, n); / *p es una variable de tipo va_list */ / *p = direcci6n primer argumento */ while (1) ( char *fJ = va_arg(p, char *); if (q) printf("%s ': q); else break; putchart n'); if (n) exit(n); Primeramente definimos la variable pyla inicializamos llamando a la macro va-start( ). Esta macro toma como argumentos la variable p y el ultimo argumento formal declarado en la funcion. La macro va_arg( ) es utilizada para coger los argumentos no nombrados, en orden. En la lla- mada, el programador debe especificar el tipo. Antes de retornar desde una funcion que ha ejecutado la macro va-start( ), debe ejecutarse la macro va_end( ). La razon es que va-start( ) puede modificar el stack de tal forma que impida un retorno satisfactorio. La macro va_end( ) deshace tales modificaciones. Sedice que una fundon es recursiva, si se llama a sl misma. El compilador C permite cualquier numero de llamadas recursivas a una funcion. Cad a vezque la fundon es llamada, los panimetros formales y las variables auto
  • 270. y register son inicializadas. Notar que las variables static solamente son ini- cializadas una vez, en la primera Hamada. La funci6n factorial, cuyo programa se presenta a continuaci6n, es recursiva. # include <stdio.h > # include <stdlib.h> long jactorial(int n); main( ) { int numero; long jac; system(' 'cls"); printj('';, Ntimero ? "); scanj(H%d'; &numero); jac = factorial(numero); printj(H nEI jactorial de %2d es %ld n'; numero, jac); if (n = = 0) return 1; else return n 4actorial(n-1); long jactorial(int n) { En el esquema siguiente se ve el proceso seguido por la funci6n, du- rante su ejecuci6n para n =4. factorial( 4) 4 * factorial(3) 3 * factorial(2) 2 * factorial(1) 1 * factorial(O) factorial(O) 24 4 * 6 3 * 2 2 * 1 1 * 1 I
  • 271. Cada Hamada a la funci6n factorial aumenta en una unidad el nivel de recursi6n. Cuando se Hega a n = 0, se obtiene como resultado el valor 1y se inicia la vuelta hacia el punta de partida, reduciendo el nivel de re- cursi6n en una unidad cada vez. Los algoritmos recursivos son particularmente apropiados cuando el problema a resolver 0 10sdatos a tratar se definen en forma recursiva. Sin embargo, el uso de la recursi6n debe evitarse cuando haya una soluci6n obvia por iteraci6n. En aplicaciones pnicticas es imperativo demostrar que el nivel maxi- mo de recursi6n es, no s610 finite, sino realmente pequeno. La raz6n es que, por cada ejecuci6n recursiva de la funci6n, se necesita cierta cantidad de memoria para almacenar las variables locales y el estado en cursa del proceso de calculo con el fin de recuperar dichos datos cuando se acabe una ejecuci6n y haya que reanudar la anterior. Como hemos indicado anteriormente, elHamar a una funci6n recursiva- mente, consume mucho espacio de pila debido a que par cada Hamada las variables que intervienen en la funci6n son salvadas en la pila para poste- riormente poder iniciar la vuelta. Esto indica que puede ser necesario ajus- tar el tamano de la pila mediante la opci6n IF de la orden CL. Tenemosdos listas de palabras clasificadas en orden ascendente. Se pide realizarun programa que fusione ambas listas, en otra tambien clasificada.
  • 272. # include <stdio.h> # include <stdlib.h> # include <string.h> # define dimA 12 # define dimN 8 # define dimF 20 main( ) [ char listaFinal[dimF][60]; int ind, r; static char listaActual[dimA][60] = [ <~na': "Carmen': "David': "Francisco': "Javier': "Jesus': "Jose': "Josefina': "Luis': "Marfa': "Patricia': "Sonia"}; static char listaNueva[dimN][60] = [ <~gustfn': "Belen': "Daniel': "Fernando': "Manuel': "Pedro': "Rosa': "Susana"}; system("cls "); if (r) [ for (ind = 0; ind < 20; ind + +) printj("%s n': listaFinal[ind]); } else
  • 273. int jusionar(char listaA[ ][60J, char listaN[ ][60J, char listaF[ ][60]) ( int ind, indA = 0, indN = 0, indF = 0; if (dimF < dimA + dimN) retu rn (0); while (indA < dimA && indN < dimN) if (strcmp(listaA[indAJ, listaN[indN]) < 0) strcpy(listaF[indF + +J, listaA[indA + +]); else strcpy(listaF[indF + +J, listaN[indN + +]); / * Los dos lazos siguientes son para prever el caso de que, * 16gicamente una lista jinalizard antes que otra. */ for (ind = indA; ind < dimA; ind + +) strcpy(listaF[indF + +J, listaA[ind]); for (ind = indN; ind < dimN; ind + +) strcpy(listaF[indF + +J, listaN[ind]); return (1); ) Realizar un programa que cuente el numero de veces que aparece cada una de las letras (a - z) en un texto introducido por tec1ado. El texto queda- ni almacenado en un array.
  • 274. / * Contar el numero de veces que aparece cada letra * en una cadena de caracteres. */ # include <stdio.h> # include <stdlib.h > # include <ctype.h > # include <string.h> main( ) / * Funci6n Principal */ { static iut contaf'z'- 'a' +Ij; / * Un array estdtico es inicializado automdticamente a ceros */ char texto[IOOOj,car; iut i = 0; system("cls"); /********************* l?ntrada de datos *********************/ printj("Introducir texto. Finalizar con AZ. n n"); while ((texto[i + +j = getchar( )) != l?OF); texto[i] = ' 0'; for (car = 'a'; car < = 'z'; car+ +) printj(" %c'~ car); printj(" n --------------------------------------------------------------- n n"); for (car = 'a'; car < = 'z'; car+ +) printj("%2d'~ conta[car- 'a']); putchar(' n '); l
  • 275. 1************************************************************** Contar Letras **************************************************************/ void ContarLetras(char text[ J, int conta[ J) { int i; for (i = 0; i < strlen(text); i+ +) if (tolower(text[i]) > = ca' && tolower(text[i]) < = Cz') conta[text[iJ - ca']+ +; El calendario Gregoriano actual obedece a la reforma del calendario julia- no que orden6 el papa Gregorio XIII en 1582. Se decidi6, despues de algu- nas modificaciones, que en 10 sucesivo fuesen bisiestos todos los alios mul- tiplos de cuatro, pero que de los alios seculares (Ios acabados en dos ceros) s6lo fuesen bisiestos aquellos que fuesen multiplos de cuatrocientos. En base a estos conceptos, construir un programa para que dada una fecha (dia, mes y alio), nos devuelva como resultado el correspondiente dia de la semana. La descomposici6n en subproblemas que se ha hecho en la realizaci6n de este ejercicio es la siguiente: Dia de la semana Entrada datos Validar datos Inicializar variables Determinar dia de la semana
  • 276. **************************************************************/ /* Dada una jecha (dia, mes, ana) * indicar el dfa correspondiente de la semana. */ # include <stdio.h > # include <stdlib.h > void LeerFecha (jot ~ia, iot *mes, iot ~nno); void EscribirFecha ( iot dd, iot mm, iot aa); void EntradaDatos (jot ~ia, iot *mes, iot *anno); iot DatosValidos (jot dia, iot mes, iot anno); iot AnnoBisiesto (jot anno); iot DiaSemana (jot dia, iot mes, iot anno); LeerFecha(&dia, &mes, &anno); EscribirFecha(dia, mes, anno); J main() / * Funci6n Principal */ [ iot dia, mes, anno; void LeerFecha (jot ~ia, iot *mes, iot ~nno) [ iot datos_validos; EntradaDatos(dia, mes, anno); datos_ validos = Datos Validos(*dia, *mes, *anno); J while (!datos_validos); J
  • 277. void EntradaDatos(int ..dia, int *mes, int ~nno) [ systemt <cls''),o printj(ffDfa (1 - 31) "),o scanj(ff%d': dial; printj(ffMes (1 - 12) "),o scanj(ff%d': mes),o printf(ffAno (1582 -- » "),o scanj(ff%d': anno),o l int DatosValidos(int dia, int mes, int annal [ int r, annoB, mesB, diaB,o annoB = (anno > = 1582); mesB = (mes > = 1) && (mes < = 12); switch (mes) [ case 2: if (r = AnnoBisiesto(anno)) diaB = (dia > = 1) && (dia < = 29); else diaB = (dia > = 1) && (dia < = 28); break; case 4: case 6: case 9: case 11: diaB = (dia > = 1) && (dia < = 30); break; default: diaB = (dia > = 1) && (dia < = 31); l if (!(diaB && mesB && annoB)) [ printj(ff nDATOS NO VAL/DOS n n"),o printj(ffPu/se una tecla para continuar "),o r = getch( ),o return (0); l else return (1); int AnnoBisiesto(int annal [ int verdad = 1, fa/so = 0;
  • 278. HSdbado': HDomingo': 'Tunes': HMartes': HMiercoles': HJueves': HViernes" }; HEnero': HFebrero': HMarzo': HAbril': "Mayo': HJunio': HJulio': '~gosto': HSeptiembre': HOctubre': HNoviembre': HDiciembre"}; if ((anno % 4 = = 0) && (anno % 100/= 0) II (anno % 400 = = 0)) return (verdad); else return (falso); void EscribirFecha(int dd, int mm, int aa) { int d; static char dia[7][1O] d = DiaSemana(dd, mm, aa); printf(" n%s %d de %s de %d n':dia[d], dd, mes[mm-1], aa); 1 int DiaSemana(int dia, int mes, int anno) { if (mes < = 2) { mes = mes + 12; anno = anno - 1; } return ((dia+2*mes+3 4mes+ 1)/5+anno+anno/4-anno/100+ anno/400+2) % 7); Igual que sucedia con 10s arrays, el nombre de una funcion representa la direccion de comienzo de la funcion; por 10tanto, puede ser utilizado para pasarl0 a funciones, colocarlo en arrays, etc.
  • 279. p-.-identif identifica a una variable tipo puntero. Esta variable recibini un puntero a una funci6n, dado por el propio nombre de la funci6n. En el siguiente ejemplo, se define un puntero p a una funci6n. A con- tinuaci6n, se asigna a p la direcci6n de la funci6n escribir y se llama a la funci6n mediante la sentencia: (*p)(5);. # include <stdio.h > void escribir(int); main( ) { void (*p)(int); p = escribir; (*p)(5); / *p = direcci6n de fa funci6n */ / * llamada a fa funci6n */ void escribir(int a) { printj(f'%d n': a); J EI nombre de una funci6n representa la direcci6n de comienzo de la misma. EI siguiente ejemplo clarifica esta caracteristica de C. EI programa que semuestra a continuaci6n, busca y escribe el valor menor de una lista de datos numerica 0 de una lista de datos alfanumerica. Si en la linea de 6r- denesse pasa un argumento "n", se interpreta la lista como numerica; en 10sdemas casos se interpreta como alfanumerica. EI programa consta fundamentalmente de una funci6n fmenor( ) que buscaen una lista de datos, el menor, independientemente de las operacio- nesde comparaci6n (numeric a 0 alfanumerica). Para ello Ie pasamos fun-
  • 280. ciones diferentes para comparar. Para hacerlo numericamente, se utiliza la funcion compnu( ) y para comparar alfanumericamente, se utiliza la fun- cion compal( ). # include <stdio.h> # include <std!ib.h> # include <string.h> # include <math.h > # define FMAX 100 main(int argc, char ~rgv[ ]) [ char *pcadena[FMAX]; char dato[81]; char *p; char *compnu(char *, char *); char *compal(char *, char *); char 4menor(int, char **, char *(*)(char *, char *)); int c = 0, longitud; / * array de punteros a los datos */ /* dato */ / * Leer !ista de datos numericos 0 a/fanumericos */ printjt'Entrar datos y fina!izar con Enter n n"); while (c < FMAX) [ printj(HDato %d: ': c + 1); if ((longitud = strlen(gets(dato))) 0) break; else [ p = (char *)malloc(longitud +1); if (p = = NULL) [ printj(Hlnsuficiente espacio de memoria n"); exit(l); } else [ strcpy(p, dato);
  • 281. pcadena[c+ +] = p; l l l /* argv[l] 1= "n" -> busqueda en una !ista alfanumerica, * argv[l] = "n" -> busqueda en una !ista numerica */ if (argc > 1 && argv[l][O] = = 'n') p = jmenor(c, pcadena, compnu); else p = jmenor(c, pcadena, compal); printf(" n nEl elemento menor de la !ista es: %s n': p); l char 4menor(int c, char *pcadena[ ], char *(*comparar)(char *, char *)) / * Buscar el dato menor de una lista */ [ char *menor; menor = *pcadena; / * menor = primer dato */ while ( --c > 0) / * comparar men or con el siguiente dato */ menor = (*comparar)(menor, * + +pcadena); return (menor); l char *Compnu(char *px, char *py) / * Camparar dos datos numericamente */ I if (ataf(px) > atof(py)) return (py); else return (px); char *compal(char *px, char *py) / * Camparar dos datos alfanumericamente */ I if (strcmp(px, py) > 0) return (py);
  • 282. else return (px); p = fmenor(c, pcadena, compnu); p = jmenor(c, pcadena, compa/); char 4menor(int c, char *pcadena[ }, char *(*comparar)(char *, char *)) Los panimetros actuales compnu y compa/ son punteros alas funcio- nes del mismo nombre. EI panimetro formal correspondiente, (~omparar)() dice que comparar es un puntero a una funcion que devuelve un puntero a una cadena de caracteres. Cuando el panimetro pasado es compnu, se ejecuta la funcion compnu( ) y cuando el panimetro pasado es compal, se ejecuta la funcion compa/( ). En este capitulo hemos estudiado como el usuario puede definir sus pro- pias funciones. No obstante C dispone en sus librerias de mas de 400 fun- ciones; algunas de ellas ya las hemos visto, como las funciones para entra- da/salida, las funciones para manipular cadenas de caracteres etc., y otras las iremos viendo en este y en sucesivos capitulos. Las declaraciones para las funciones matematicas que a continuacion se describen, estan en el fichero a incluir math.h. Quiere esto decir, que cuan- do se utilice una funcion matemcitica en un programa, debe especificarse la directriz:
  • 283. Los argumentos para estas funciones son de tipo double y el resultado devuelto es tambien de tipo double. Por ello, en muchos casos utilizare- mos la construcci6n cast para convertir explicitamente los argumentos al tipo deseado. La expresi6n (double) hace que valor sea convertido a tipo double an- tes de ser utilizado. Podemos clasificar las funciones matemcHicas en las siguientes cate- gorias: Esta funci6n da como resultado el arco, en el rango 0 a 1f, cuyo coseno es x. EI valor de x debe estar entre -1 y 1; de 10 contrario se obtiene un error DOMAIN (argumento fuera del dominio de la funci6n). Esta funci6n da como resultado el arco, en el rango -1f12 a 1f12, cuyo seno esx. EI valor de x debe estar entre -I y 1, si no se obtiene un error DOMAIN (argumento fuera del dominie de la funci6n).
  • 284. Esta fund6n da eomo resultado el areo, en el rango -7r12 a ni2, euya tan- gente es x. Esta funei6n da eomo resultado el areo, en el rango -7r a 7r, euya tangente es y/x. Si ambos argumentos son 0, se obtiene un error DOMAIN (argu- mento fuera del dominio de la fund6n). # include <math.h > main( ) { double valor -1; do { printf(H%lf %If n': acos(valor), atan2(valor, 1.0)); valor + = 0.1; } while (valor < = 1.0); }
  • 285. Esta funci6n da como resultado la tangente de x (x en radianes). double tan(double x); Esta fund6n da como resultado el coseno hiperb6lico de x (x en radianes). double cosh(double x); Esta funci6n da como resultado el seno hiperb6lico de x (x en radianes). double sinh(double x); Esta funci6n da como resultado la tangente hiperb6lica de x (x en radianes). double tanh(double x); Esta fund6n da como resultado el valor de eX (e double exp(double x); Esta fund6n da como resultado el logaritmo natural de x. double log(double x);
  • 286. Esta funci6n da como resultado un valor double, que representa el entero mas pequeno que es mayor 0 igual que x. double x = 2.8, y = -2.8; printj(<t%g %g n': cei/(x), cei/(y)); Esta funci6n da como resultado el valor absoluto x. El argumento x, es un valor real en doble precisi6n. Igualmente, abs y labs dan el valor abso- luto de un int y un long respectivamente. Esta funci6n da como resultado un valor double, que representa el entero mas grande que es menor 0 igual que x. double x = 2.8, y = -2.8; printj(<t%g %g n': jloor(x), jloor(y));
  • 287. Esta funcion da como resultado xY• Si x es 0 e y negativo; 0 si x e y son 0; 0 si xes negativo e y no es entero, se obtiene un error DOMAIN (argu- mento fuera del dominio de la funcion). Si xY da un resultado superior al valor limite para el tipo double, el resultado es este valor limite (1.7976ge +308). double x = 2.8, y = -2.8; printf("%g n': pow(x, Y)); Esta funcion da como resultado la raiz cuadrada de x. Si x es negativo, ocurre un error DOMAIN (argumento fuera del dominio de la funcion). Las funciones de la libreria matematica Haman a la funcion matherr cuan- do ocurre un error. struct exception [ int type; char *name; double argl, arg2; double retval; } .x; tipo de error funcion donde se origina el error valores que causan el error valor devuelto
  • 288. DOMAIN OVERFWW EI argumento esta fuera del dominie de la funci6n. EI resultado es demasiado grande para ser repre- sentado. Perdida PARCIAL de significaci6n. Un argumento de la funci6n ha tornado un valor no permitido. Perdida total de significaci6n. EI resultado es demasiado pequeno para ser repre-' sentado. PWSS SING TWSS UNDERFWW En el ejemplo siguiente se muestra la forma de utilizar la funci6n mat- herr(), la cual sera Hamada automaticamente si"alguna funci6n matemati· ca da lugar a un error. # include <stdio.h> # include <math.h > # include <string.h > main( ) { struct exception *X; double a = -2, b = 5, c = 0, Ig; printj(H%g n n': log(a)); printj(H%g n n': loglO(b)); Ig = log(c); if (Ig != -1) / * -1: error; valor devuelto por matherr */ printf(H%g n n': Ig);
  • 289. int matherr(struct exception *x) ( if (x- > type = = DOMAIN) ( if (strcmp(x- >name, "log") = = 0) x- > retval = log(-(x- > arg1)); else if (strcmp(x- >name, ''log10'') = = 0) x- >retval = log10(-(x- >arg1)); printj("Arg. negativo: %s(%g) n'~ x- > name, x- > arg1); printf("Se calcula 19(%g): '~ -x- > arg1); ] if (x- >type = = SING) ( printf("Argumento no permitido. n"); printf("log(O) = -00 n"); x- >retval = -1; ] ] Esta fundon da como resultado un numero pseudoaleatorio entero, entre o y 32767. Esta funcion fija el punta de comienzo para generar numeros pseudoalea- torios. Si no se utiliza, e1punta de comienzo siempre es el mismo para cada ejecuci6n, que es el correspondiente a un argumento de valor 1.
  • 290. Esta funcion indica el tiempo empleado por el procesador en el proceso Hamado, en el momento que la fundon clock( ) es ejecutada. Este tiempo es dado en milesimas de segundo. EI tiempo en segundos, es el resultado de dividir el valor devuelto por clock( ), entre el valor de la macro CLK_TCf(, 0 la macro CLOCK---.PER_SEC (version ANSI), ambas de valor 1000. Si este tiempo no esta disponible 0 si su valor no puede ser representado, la funcion devuelve un valor -1, bajo el tipo· clock_1 ( (clock_t)-l ) definido de la forma: typedej long clock_t;. Esta funcion da como resultado el numero de segundos transcurridos des- de las 0 horas dell de Enero de 1970. Esta funcion convierte un tiempo almacenado como un valor de tipo time_I, a una cadena de caracteres de la forma:
  • 291. Esta funci6n devuelve un puntero a la cadena de caracteres resultante; o un puntero nulo (NULL), si t representa un dato anterior a 1980. # include <stdio.h> # include <stdlib.h> # include <time.h > main( ) [ int x, tm; time_t segundos; time(&segundos); printf(" n%s n': ctime(&segundos)); srand((unsigned)(segundos % 65536)); for (x 1; x < = 5; x+ +) [ do / * tiempo de espera igual a 1 segundo */ tm clock( ); while (tm/CLK_TCK < x); / * se genera un numero aleatorio cada segundo */ printft'Iteraci6n %2d, nro. aleatorio = %d n': x, rand( )); J J Esta funci6n convierte el numero de segundos transcurridos des de la 0 ho- ras dell de Enero de 1970, valor obtenido generalmente por la funci6n timer ), ala fecha y hora correspondiente (corregida en funci6n de la zona horaria en la que nos encontremos). EI resultado es almacenado en una estructura de tipo tm, definida en time.h.
  • 292. La funcion loealtime devuelve un puntero a.la estructura que contiene el resultado, 0 un puntero nulo si el tiempo no puede ser interpretado. tm_see tm-'llin tm_hour tm_mday tm_mon tm_year tm_wday tm_yday # include <stdio.h> # include <time.h > Segundos (0 - 59) Minutos (0 - 59) Horas (0 - 23) Dia del mes (1 - 31) Mes (0 - 11; Enero = 0) Ano (actual menos 1900) Dia de la semana (0 - 6; Domingo = 0) Dia del ano (0 - 365; 1 de Enero = 0) main( ) { struet tm 4h; time_t segundos; timer&segundos); fh = /ocaltime(&segundos); printj(H%d horas, %d minutos n': fh- > tm_hour, fh- > tm_min); } Esta funcion da como resultado el cociente y el resto de la division de DU· merador entre denominador.
  • 293. La funci6n div() devuelve el resultado (cociente y resto) en la estruc- tura div_t,. definida en el fichero stdlib.h. typedef struct _div_t [ int quot; int rem; ] div_t; / * cociente */ /* resto */ La funci6n ldiv() realiza la misma funci6n que div(), pero para valo- res de tipo long. EI resultado es devueIto en la estructura Idiv_t, definida en el fichero stdlib.h. typedef struct _ldiv_t [ long quot; / * cociente */ long rem; / * resto */ ]ldiv_t; Esta funci6n ordena un array, utilizando el algoritmo quick-sort. La fun- cion no retorna -un valor.
  • 294. comparar es un puntero a una funci6n definida por el usuario, que com- para dos elementos y retorna un valor: < 0 si el elemento1 es menor que el elemento2, > 0 si el elemento1 es mayor que el elemento2 = 0 si el elemento1 es igual al elemento2. La clasificaci6n que se obtiene es en orden ascendente. Si queremas una clasificaci6n en orden descendente, invertir los valores devueltos par la funci6n comparar( }, para mayor que y menor que. # include <stdio.h> # include <std!ib.h> # define N 100 / * nlimero maximo de elementos para el array */ / * necesaria para qsort. */ / * Compara dos elementos */ ascendente, -1 = descendente */ main( ) / *funci6n principal */ [ int !ista[N]; / * array de elementos a ordenar */ int r, i = -1; do [ systemt 'cls"}; printjt'(, Como desea la ordenaci6n ? n n"}; pr,intj("Ascendente = 1, Descendente = -1 = > "}; scanf("%d': &asc_des}; } while (asc_des != 1 && asc_des != -1); printj(" nlntroducir !ista de nlimeros a ordenar. n"}; printj("Fina!izar con con un caracter no numerico. n n "}; while (scanf("%d': &!ista[+ +i]) != 0 && i < N};
  • 295. / * Clasijicaci6n ut!izando el algoritmo Quicksort */ qsort(!ista, i, sizeof(!ista!O]), comparar); printjt' nLista de valores ordenada: n n "); for (r = 0; r < i; r+ +) printj("%d ': !ista!r]); int comparar(int ~rgl, int ~rg2) { if (~rgl < ~rg2) return (-asc_des); else ij (~rgl > *arg2) return (asc_des); else return (0); Este programa lee una lista de numeros y la clasifica, utilizando la fun- cion qsort( ), en orden ascendente 0 descendente. La funcion qsort( ), implementada en C, recibe como panimetros: la direccion de comienzo del array, el numero de elementos a ordenar, la lon- gitud en bytes de cada elemento y el nombre de una funcion implementada por el usuario para comparar dos elementos. El nombre de una funcion esuna constante de valor, la direccion de comienzo de la funcion. Esta fun- cion es Hamada por qsort( ) y en la Hamada Ie pasa las direcciones de los doselementos a comparar, segun se ve en la definicion; de ahi que, en nuestro caso,los argumentos de la funcion comparar( ) sean dos punteros a enteros. Esta funcion ejecuta una busqueda binaria del objeto, en un array clasi- ficado. #include <stdlib.h > 0 <search.h > void *bsearch(const void *objeto, const void .base, size_t num, size_t bytes, comparar);
  • 296. < 0 si el elemental es menor que el elemento2, > 0 si el elemental es mayor que el elemento2 = 0 si el elemental es igual al elemento2. es un puntero a una funci6n definida por el usuario, que com- para dos elementos y retorna un valor: Esta funci6n retorna un puntero a la primera ocurrencia de objeto en el array apuntado por base. Si la busqueda falla, la funci6n retorna un NULL. (Ver tambien la fund6n qsort( )." Ifind(objeto, base, num, bytes, comparar) Isearch(objeto, base, num, bytes, comparar) Las funciones ljind( ) y lsearch( ), ejecutan una busqueda lineal de objeto en un array no necesariamente clasificado. Si objeto no se encuentra sobre el array, la funci6n lsearch() 10 afiade al final de este, mientras que ljind() no. void *lfiild(const void *objeto, const void *base, unsigned int num, unsigned int bytes, comparar); void *Isearch(const void *objeto, const void *base, unsigned int num, unsigned int bytes, comparar);
  • 297. es un puntero a una funci6n definida por el usuario, que com- para dos elementos y retorna un valor: != 0 si el elemental y el elemento2, son distintos, = 0 si el elementol es igual que el elemento2. Estas funciones retornan un puntero a la primera ocurrencia de obje- to en el array apuntado por base. Si la busqueda falla, la funci6n lfind( ) retorna un NULL, y lsearch( ) retorna un puntero al elemento afiadido al final del array. # include < stdio.h > # include <search.h> int comparar(int *, int *); / * necesaria para lsearch. */ / * Compara dos elementos */ / *funci6n principal */main() [ static int lista[ j = [24, 15, 5, 69, 43, 24, 2, 1, 8, 10, 13}; / * n: mlmero de elementos del array */ unsigned n = sizeof(lista)/sizeof(lista[Oj); int v; / * objeto a buscar */ int r = 0; int *p; system("cls "); printj("%d n':n); printf(";, Valor a buscar ? "); scanf("%d': &v); / * Busqueda lineal utilizando la funci6n lsearch( ) */ p = lsearch(&v,lisla, &n, sizeof(lisla[Oj), comparar);
  • 298. printj(H nelemento encontrado/afiadido: %p %d n': p, *p); / * si el elemento es afiadido n se ve incrementado en 1 */ printj(H nLista de valores: n n "); for (r = 0; r < n; r+ +) printf(H%p %d n': lista +r, lista[r)); int comparar(int *argl, int *arg2) [ if (*argl /= *arg2) return (1); else return (0); Realizar un programa para clasificar u ordenar lineas, de modo que si se aporta el argumento -n ordene las lineas de entrada numericamente; y si no, que las ordene lexicognificamente (alfanumericamente). Una clasificaci6n u ordenaci6n se bas a en un algoritmo que realiza comparaciones e intercambios hasta que 10s elementos esten ordenados. Este algoritmo es independiente de las operaciones de comparaci6n e intercam· bio, por 10 que pasandole diferentes funciones para comparar, y si es preci· so, para intercambiar, podremos efectuar la ordenaci6n para distintos ti· pos de objetos. main( ) I
  • 299. Cuando la fund6n principal maio( ) llama a la funci6n clasificar( ), Ie pasa un puntero a la funci6n oumcmp( ), para realizar una comparaci6n numerica de dos elementos, en el caso de que la ordenaci6n sea numerica. En caso que la ordenaci6n sea alfanumerica, Ie pasa un puntero a la fun- ci6n strcmp( ) de C, para realizar una comparaci6n alfanumerica de dos elementos. En ambos cas os, cuando la funci6n maio( ) llama a la funci6n c1asificar( ), Ie pasa un puntero a la funci6n cambiar( ), para intercambiar el valor de dos elementos, independientemente de su tipo, pues 10 que in- tercambiamos son las direcciones a estos objetos, en un array de direccio- nes 0 punteros. # include <stdio.h> # include <conio.h > # include <ctype.h > # include <string.h> # define LINEAS 100 main(iot argc, char *argv[J) I char *plinea[LINEAS]; iot nlineas; iot LeerLineas (char **, iot); void clasijicar(char **, iot, iot (*)( ), iot (*)( )); 1* strcmp es una funci6n de C (comparar lexicogrdficamente) *1 iot numcmp(char *, char *); 1* comparar numericamente *1 void cambiar(char **, char **); 1* funci6n de intercambio *1 void EscribirLineas(char *plinea[ ], iot nlineas); char c; I * array de punteros alas lfneas ,:,1 1* numero de lfneas lddas d system(Hcls''); printj(HLa utilizaci6n de este program a es: n n"); printjt' PROGOO.EXE -n "); printf(HI * ordena numericamente *1 n"); printj(H PROGOO.EXE "); printf(HI * ordena lexicogrdficamente *1 n n "); printf(HDesea continuar sin "); c = getch( ); if (tolower(c) /= 's') exit(O);
  • 300. systemt 'cls"); print! ("Proceso de clasificaci6n por /(neas. n n"); print! ("Introducir datos. Pulse Enter para SALIR. n"); if ((nlineas = LeerLineas(plinea, LINEAS)) > = 0) [ print! ("Proceso de clasificaci6n. n n"); if (argc > 1 && argv[I][O] = = '-' && argv[l][l] 'n') clasificar(plinea, nlineas, numcmp, cambiar); else clasificar(plinea, nlineas, strcmp, cambiar); EscribirLineas(plinea, nlineas); J else print! ("Demasiadas /(neas para clasificar n "); 1************************************************************** Funci6n leer lineas **************************************************************/ int LeerLineas (char *plinea[ ], int lineasmax) [ int longitud, nlineas = 0; char *P, linea[LONGMAX]; I * Leer una linea *1 while ((longitud = strlen(gets(linea))) > 0) { if (nlineas > = lineasmax) return (-1); 1* demasiadas [[neas a ordenar */ I. asignar espacio de memoria para la linea leida */ else if ((p = (char .)malloc(longitud+1)) = = NULL) return (-1); I. insuficiente espacio de memoria */ else ( I. copiar la lfnea en el espacio de memoria asignado */ strcpy(p, linea);
  • 301. / * guardar el apuntador a la lfnea en el array */ plinea[nlineas+ +J = p; I I return (nlineas); / * mimero de lfneas lefdas */ I /************************************************************** Funci6n clasificar **************************************************************/ / * Ordena las cadenas plinea[OJ ... plinea[NdeLineas - 1J * ascendentemente */ void clasificar(char *plinea[ J,iot NdeLineas, iot (*comp)( ), iot (*per)( )) { char wux; iot i, s = 1; while ((s 1) && (--NdeLineas > 0)) { s = 0; for (i = 1; i < = NdeLineas; i + +) if (( *Comp)(plinea[i-1J, plinea[i]) > 0) { (*per)(&plinea[i-1J, &plinea[i]); s = 1; 1************************************************************** Funci6n numcmp (comparar numericamente) **************************************************************/ iot numcmp (char >tStr1,char >tStr2) ( double atof( ), n1, n2; nl = atof(str1); n2 = atof(str2);
  • 302. if (n1 > n2) return (1); else if (n1 < n2) return (-1); else return (0); 1************************************************************** Funci6n cambiar **************************************************************/ void cambiar (char **px, char **py) ( char wux; aux *px; *px *py; *py aux; 1************************************************************** Funci6n escribir lfneas **************************************************************/ void EscribirLineas(char *plinea[ J, iot nlineas) { while (--nlineas > = 0) printj(H%s n': *plinea+ +);
  • 303. 2 Operaciones con ficheros en C •FuncionesEstandar de E/S •Funcionesde E/S de Bajo Nivel •Funcionespara la Consola y Puertos de E/S
  • 304. Las funciones de entrada y salida (E/S) de las librerias estandar de C, per- miten leer y escribir datos a, y desde, ficheros y dispositivos. Un fichero esuna colecci6n de informaci6n que almacenamos en un soporte magneti- co,generalmente un disco, para poder manipularla en cualquier momento. C tiene disponibles los tres tipos siguientes de funciones de E/S: En este capitulo se presentan las funciones estandar de E/S; su carac- teristica fundamental es que la E/S, en el procesamiento de ficheros, se rea- lizaa traves de un buffer 0 memoria intermedia. Tambien, permiten la E/S con formato. C se refiere a estas funciones como "Stream I/O". La utilizaci6n de un buffer 0 memoria intermedia para realizar las ope- raciones de E/S, es una tecnica, implementada en software, disefiada para hacerlas operaciones de E/S mas eficientes. Un buffer es un area de datos
  • 305. en la memoria (RAM)>-asignada-por el programa-qll.e bre el fichera. La utilizacion de buffers en operaciones de E/S, reduce e1numero de accesos al dispositivo fisico (disco por ejemplo) asociado con el fichero, necesarios para la transferencia de informacion entre e1programa y el fichero; un ac- ceso a un dispositivo fisico consume mucho mas tiempo que un accesoa la memoria (RAM). Cuando un fichero no tiene asociado un buffer, cada byte escrito a, 0 leido desde, el fichero es fisicamente transferido en el mo- mento de la operacion. En cambio, cuando un fichero tiene asociada un buffer, todas las operaciones de E/S requeridas son servidas desde ese buf- fer; la transferencia fisica de datos se hace en multiplos del tamafio del buffer. Las funciones eShindar de E/S, como su nombre indica, proporcionan la forma mas normal de E/S en un programa C. Permiten escribir y leer da- tos de un fichero, de las siguientes formas: Primera, los datos pueden ser escritos 0 leidos caracter a caracter can las funciones jputc( ) y jgetc( ). ~ - Segunda, los datos pueden ser escritos y leidos palabra a palabra can las funciones putw() y getw(). Se entiende por palabra, palabra maquina o valor de tipo into 3·- Tercera, los datos pueden ser escritos y leidos como cadenas de carac· teres con las funciones jputs( ) y jgets( ). ,:. Cuarta, los datos pueden ser escritos y leidos con formato, con lasfun· ciones jprintf( ) y jscanf( ). Quinta, los datos pueden ser escritos y leidos como registros a blo- ques, (esto es, como un conjunto de datos de longitud fija, tales comoes- tructuras 0 elementos de un array), con las funciones jwrite( ) y jread(t
  • 306. Para poder escribir 0 leer sobre un fichero, primeramente hay que abrirlo con las funcionesjopen(), jdopen() 0jreopen(). El fichero puede ser abierto para leer, para escribir 0 para leer y escribir; y puede ser abierto en modo texto 0 en modo binario. La necesidad de dos modos diferentes, es por las incompatibilidades existentes entre C y MS-DOS ya que C fue disefiado original mente para el sistema operativo UNIX. El modo texto es para ver los ficheros como si estuvieran bajo UNIX; y el modo binario, para verlos como si estuvie- ran bajo MS-DOS. En modo texto, un final de linea es representado en C por un unico canicter (' n'), pero en un fichero de MS-DOS es representado por dos caracteres (CR + LF). Esto significa que, bajo MS-DOS, cuando C escri- be en un fichero convierte el canicter ' n', en los caracteres CR + LF; y cuando C lee de un fichero y encuentra los caracteres CR + LF, los con- vierte a ' n'; y cuando encuentra un Ctrl +Z 10 interpreta como un EOF. En modo binario estas conversiones no tienen lugar. Cuando un programa comienza su ejecuci6n, son abiertos automiti- camente cinco ficheros, que se corresponden con dispositivos. Estos fiche- ros, direccionados por streams, y los dispositivos asociados par defecto son: ~ .Ii 1, stdin stdout stderr stdaux stdprn dispositivo de entrada estandar (teclado) dispositivo de salida estandar (pantalla) dispositivo de error estandar (pantalla) dispositivo auxiliar estandar (puerto serie) dispositivo de impresi6n estandar (impresora paralelo) De estos cinco, dos de ellos, el dispositivo serie y el dispositivo de im- presion paralelo, depend en de la configuraci6n de la maquina, por 10 tan- to pueden no estar presentes.
  • 307. Las streams especificadas anterior mente, estan declaradas como pun· teros constantes a una estructura de tipo FILE. Esta estructura define un buffer para conectar, a traves de el, la stream con el fichero fisico. Debido a esto en much as ocasiones nos referiremos a la stream como si fuera e fichero. Estas streams pueden ser utilizadas en cualquier funcion que reo quiera como argumento un puntero a un fichero. La entrada y salida es· tandar, podran ser redireccionadas utilizando los simbolos <, >, >> a (ver notas sobre DOS en el capitulo 19). Despues de haber finalizado el trabajo con un fichero, este debe cerrarse con la funcionjclose(). Si un fichero no se cierra explicitamente, es cerra· do automaticamente cuando finaliza el programa. Sin embargo, es aeon· sejable cerrar un fichero cuando se ha finalizado con el, ya que el numero de ficheros abiertos al mismo tiempo esta limitado. Las operaciones de lectura y escritura siempre empiezan en una posicion perfectamente definida en todo momento. A esta posicion la denominare· mos puntero de lectura escritura (LIE). Cada vez que se efectua una ope- racion de lectura 0 de escritura, el puntero de LIE avanza a la siguiente posicion. Cuando un fichero se abre, el puntero de LIE es posicionado auto· maticamente al principio del fichero, excepto cuando se abre para afladir informacion; en tal caso, es posicionado al final del fichero. EI puntero de LIE puede ser situado en cualquier parte del fichera, utilizando la funcion jseek( ). Para situarse al principio de un fichero se dispone de la funcion rewind( ); para determinar en que posicion nos en· contramos, tenemos la funcion jtell( ). Cuando en una operacion sobre un fichero ocurre un error, este puede ser detectado por la funcion jerror( ). Cuando ocurre un error, el indicador
  • 308. de error permanece activado hasta que el fichero se cierra, a no ser que utilieemos la funci6n clearerr( ) 0 rewind( ) para desactivarlo explicitamente. Existen tres organizaciones de ficheros basicas, de cuya combinaci6n se de- rivan multitud de organizaciones posibles. Estas son: Secuencial Aleatoria Secuencial indexada En cada caso, se elegira una u otra en funci6n de las caracteristicas de los soportes y del modo de acceso requerido. - Acceso secuencial - Acceso aleatorio 0 directo Se habla de acceso secuencial cuando se van accediendo posiciones sucesivas, esto es tras acceder a la posici6n N, se accede a la posici6n N + 1; y se habla de acceso aleatorio 0 directo cuando se accede directamente a la posici6n deseada, sin necesidad de acceder alas posiciones que Ie preceden. Un fichero en C esta organizado secuencialmente y el acceso puede ser secuencial, 0 aleatorio si utilizamos la funci6n fseek( ). Esta funci6n abre el fichero especificado por path. El argumento acceso especifica c6mo es abierto el fichero. # include <stdio.h >
  • 309. Abrir un fichero para leer. Si el fichero no existe 0 no se en- cuentra, se obtiene un error. Abrir un fichero para escribir. Si el fichero no existe, se crea; y si existe, su contenido se destruye para ser creado de nuevo. Abrir un fichero para afiadir informaci6n al final del mismo. Si el fichero no existe, se crea. Abrir un fichero para leer y escribir. Si el fichero no existe, se crea; y si existe, su contenido se destruye para ser creado de nuevo. Abrir un fichero para leer y afiadir. Si el fichero no existe, se crea. Alas formas de acceso mencionadas, se les puede afiadir un canicter to b (rb, a + b 0 ab +, etc.), para indicar si el fichero se abre en modo texto o en modo binario. La opci6n t, no pertenece allenguaje C estandar; sino que es una extensi6n de Microsoft. Si t 0 b no se especifican, el modo (O_TEXTu O---.BINARY) es definido por la variable global-fmodede C (O_TEXT por defecto). La funcionjopen( ) devuelve un puntero a una estructura de tipo FILE, la cual se corresponde con el buffer asociado con el fichero abierto. Un puntero nulo indica un error. Este puntero es utilizado por las funciones C, para leer y escribir datos en un fichero. Por eso antes de utilizar la fun· ci6njopen(), debemos definir un puntero de tipo FILE, tipo que esta de- clarado en el fichero stdio.h. Para simplificar nos referiremos a ese pun/e- ro, diciendo que apunta al fichero abierto.
  • 310. # include < stdio.h > FILE *pj; pf = jopen("datos': "w"); Este ejemplo indica que se abre el fichero datos para escribir, y que sera referenciado por el puntero pf Debe especificarse el fichero de cabecera stdio.h, porque contiene la declaraci6n de FILE. Esta es de la forma siguiente: struct _iobuj [ char *-ptr; int _ent; char *_base; char -flag; char -file; ); typedef struct _iobuj FILE; La variable pj, declarada en el ejemplo anterior, contiene la direcci6n de memoria (puntero) de un elemento del array de estructuras _iob[ J; esta asignaci6n ocurre, por cad a fichero que se abre. El array _iob[ J, tiene un numero de elementos igual al valor especificado por la variable FILES declarada en el fichero de configuraci6n del sistema, CONFIG.SYS. Como ejemplo, observar como estan definidas las streams estandar. # define stdin # define stdout # define stderr # define stdaux # define stdprn (&_iob[OJ) (&_iob[lJ) (&_iob[2J) (&_iob[3J) (&_iob[4J) Asocia una stream con un numero de fichero, num, resultante de haber abierto el fichero con la funci6n a nivel de sistema open( ) (ver capitulo
  • 311. siguiente). Esto nos permite procesar el fichero como si hubiera sido ha· bierto por la funci6n jopen( ). La descripci6n para el argumento acceso, es la misma que la dada en la funci6n jopen( ). La funci6n jdopen( ) devuelve un puntero al fichero abierto por ella. Un puntero nulo indica un error. # include <stdio.h> # include <jcntl.h> # include <io.h > FILE *pj; iot nj; nj = open(<<datos': O-.RDONLY); pj = jdopen(nj, «r"); Esta funci6n cierra el fichero actualmente asociado con el puntero pf; j reasigna pf, al fichero identificado por path. Es utilizada para redireccio· nar stdin, stdout, stderr, stdaux y stdprn, a ficheros especificados por el usuario. La descripci6n para el argumento acceso, es la misma que la dada en la funci6n jopen( ). La funci6n jreopen( ) devuelve un puntero al fichero abierto nueva· mente. Si ocurre un error, el fichero original es cerrado y se devuelveun puntero nulo.
  • 312. # include <stdio.h > FILE *pj; pj = jreopen("datos': "w': stdout); Este ejemplo, reasigna stdout al fichero Hamado datos. Ahora, 10 que escribamos en stdout, sera escrito en datos. Esta funci6n cierra el fichero apuntado por pf. Cualquier dato en el buffer asociado, se escribe en el fichero antes de cerrarlo. Si el fichero es cerrado, la funci6njclose( ) devuelve un cero. Si ocurre un error entonces devuelve un EOP. La funci6njcloseall() devuelve un entero igual al numero de ficheros cerrados. Si ocurre un error, entonces devuelve un EOP.
  • 313. Esta funci6n verifica si ha ocurrido un error en una operaci6n con fiche· ros. Cuando ocurre un error, el indicador de error para ese fichero se pone activo y permanece en este estado, hasta que sea ejecutada la funci6n clea- rerr( ). La funci6n jerror( ), devuelve un cero si no ha ocurrido un errory un valor distinto de cero en caso contrario. Esta funci6n desactiva el indicador de error y el indicador de fin de fiche- ro (EOF) para un determinado fichero, poniendolos a valor O. FILE *pj; char *cadena "Esta cadena nunca sera escrita"; main( ) { pj = jopen("datos': "r"); jprintf(pf, "%s n': cadena); if (ferror(pf)) { jprintj(stderr, "Error de escritura n");
  • 314. clearerr(pf); J jclose(pj); J Este programa abre el fichero llamado datos para leer y a continua- cion se intenta escribir. La funci6njerror() detecta el error, manda un men- saje por la consola y desactiva el indicador de error para ese fichero. La funci6n jeof( ) devuelve un valor distinto de 0, cuando se intenta leer un elemento del fichero y nos encontramos con un eof (end of file - fin de fichero). En caso contrario devuelve un O. while (!feof(pf)) ! printj ("Denominacion: printj ("Precio: %s n': reg.denomi); %d n n': reg.precio); / * Leer el siguiente registro del jichero */ jread (&reg, sizeof(struct registro), 1, pf); J jclose(pf); Este ejemplo dice: mienttas no se llegue al final del fichero, leer regis- tros y escribirlos por la pantalla. Los registros leidos son estructuras. Des- pues de haber leido el ultimo registro, observamos que es necesario hacer una nueva lectura para activar el indicador eof.
  • 315. if ((pj = jopen("datos': "r")) perror("Fichero no abierto"); Esta funci6n escribe en la salida estandar stderr, el mensaje dado par ca· dena seguido por dos puntos, el mensaje de error dado por el sistema) un caracter NL. EI numero de error es almacenado en la variable del sistema errn~ la cual seria declarada a nivel externo. La variable del sistema sys_erriisl es un array que contiene los mensajes de error ordenados por el numero de error. La funci6n perror( ) busca el mensaje de error en esta variable utilizando el valor de la variable errno como indice. int errno; int sys_nerr; char *sys_errlist[sys_nerr]; Esta funci6n escribe un caracter car en la posici6n indicada por el puntero de LIE del fichero apuntado por pf.
  • 316. La funci6n jpute( ), devuelve el canicter escrito 0 un EOF si ocurre un error. No obstante, ya que EOF es un valor aceptado por car, utilizar la funci6n jerror( ) para verificar si ha ocurrido un error. La macro pute desarrolla la misma funci6n y de la misma forma que la fund6n jpute( ). # include <stdio.h > # include <string.h > FILE *pj,o char bujjer[81},o int i = 0; main( ) ( / *Abrir ef jiehero "texto" para escribir */ if ((pj = jopen("texto': "w")) = = NULL) ( perror("Ef jiehero no se puede abrir"),o exit(l),o J strepy(bujjer, "Este es un texto para jpute!! n "),o while (fjerror(pf) && bujjer[i]) jpute(bujjer[i + +}, pf),o if (ferror(pf)) perror("Error durante fa eseritura "),o jclose(pj),o J
  • 317. / * Abrir el fichero "texto" para leer */ if ((pf = Jopen("texto'~ "r")) = = NULL) { perror("EI fichero no se puede abrir"); exit(l); J while (fjerror(pf) && fjeof(pf)) buffer[i + +] = fgetc(pf); buffer[--i] = C 0'; Esta funci6n lee un cankter, del fichero apuntado por pf, de la posicion indicada por el puntero de LIE. La funci6n fgetc( ) devuelve el canicter leido 0 un EOF si ocurre un error 0 se detecta el final del fichero. No obstante, ya que EOF es un valor aceptado, utilizar la funci6n ferror( ) 0 feof( ) para distinguir si se ha de· tectado el final del fichero 0 si ha ocurrido un error. La macro getc desarrolla la misma funci6n y de la misma forma que la funci6n fgetc( ). FILE *pf; char buffer[81J; iot i = 0;
  • 318. if (ferror(pf)) perror(<eError durante la lectura "); printjt'%s': bujjer); l El siguiente programa lee el texto contenido en un fichero pasado como argumento en la linea de 6rdenes, y da como resultado el numero de carac- teres de dicho fichero. /************** Contar los caracteres de un jichero **************/ / * CCONTA.C */ # include <stdio.h> # include <stdlib.h> main(int argc, char *argv[J) ( FILE *pj; int conta = 0; system(<ecls"); / * Comprobar el nOde argumentos pasados en la linea de ordenes */ if (argc /= 2) ( printf(<ePormato: C> cconta nombre-fichero n"); exit(l); l / * Abrir el jichero indicado por argv[l] */ if ((pj = jopen(argv[l], <er")) = = NULL) ( printj(<eEIjichero %s no puede abrirse n': argv[IJ); exit(l);
  • 319. while (fjerror(pf) && fjeof(pj)) [ } if (ferror(pj)) perror(UError durante la lectura "); jclose(pf); print/fuEl jichero %s tiene %d caracteres n': argv[l],conta-l); } Esta funci6n escribe un valor binario entb de tipo int, en el fichero apun- tado por pf. La funci6n putw( ) devuelve el valor escrito 0 un EOP si ocurre un error. No obstante, ya que EOF es un valor valido, utilizar la funci6n fe- rror( ) para verificar si ha ocurrido un error. Esta funci6n lee el siguiente valor binario de tipo int, del fichero apuntado por pf y avanza el puntero de LIE al siguiente valor no leido. La funci6n getw( ) devuelve el valor leido 0 un EOF si ocurre un error o se detecta el final del fichero. No obstante, ya que EOF es un valor vali- do, utilizar la funci6n jerror( ) 0 jeof( ) para distinguir si se ha detectado el final del fichero 0 si ha ocurrido un error.
  • 320. main( ) [ static int !ista[ J int elementos int i; = { -1, 10, 20, 30, 40, 50 }; sizeof (!ista)Isizeof (int); 1* Abrir el jichero para leer y escribir *1 pf = jopen(Hdatos.bin': HW+ "); 1* Escribir el array de enteros en el jichero *1 for (i = 0; i < elementos; i+ +) [ putw(!ista[i], pj); if (ferror(pf)) { perror(HError durante la escritura"); exit(l); } J 1* Posicionar el puntero de LIE al principio *1 rewind(pj); 1* Escribir el contenido del jichero *1 while (1) [ i = getw(pf); if (feoj(pj) II jerror(pf)) break;
  • 321. printf(H%d ': i); 1 if (ferror(pf)) perror(HError durante fa fectura"); jclose(pf); 1 Este programa escribe en el fichero datos.bin, en binario, el contenid de un array de enteros llamado !ista; y a continuaci6n visualiza el conteni do de dicho fichero. Observar que para escribir el contenido del ficher primeramente hay que situar el puntero de LIE al principio del mismo. Esta funci6n copia la cadena de caracteres, cadena, en el fichero apuntado por pf. La terminaci6n ' 0' no se copia. La funci6njputs(), si no hay error, devuelve un O. En caso contrario, devuelve un valor distinto de O. Para recuperar de una forma sencilla la informaci6n escrita en el fi· chero, es aconsejable copiar el canicter ' n' despues de cada cadena es· crita sobre el fichero. while (gets(cadena) != NULL) { jputs(cadena, pj); jputc(' n: pj); 1
  • 322. Esta funci6n lee una cadena de caracteres, cadena, del fichero apuntado por pf. La terminaci6n ' 0' es afiadida automaticamente a la cadena lei- da. Se entiende por cadena desde la posici6n actual dentro del fichero, hasta el primer canicter nueva linea (' n') incluido este, hasta el final del fiche- ro, 0 hasta que el numero de caracteres sea igual a n-1. La funci6n fgets( ) devuelve la cadena leida. Si el valor devuelto es NULL, quiere decir que ha ocurrido un error 0 que se ha detectado un EOP. Utilizar feof( ) 0 ferror( ) para determinar 10 que ha ocurrido. # include <stdio.h> # include <stdlib.h> # define N 81 main( ) ( FILE *pf; char buffer[NJ, f[13J; system (Hcls"); printf(HNombre del fichero: "); gets(f); /[12J = ' 0'; / * truncar nombres superiores a 12 caracteres */ / * Abrir en modo binario el fichero f para escribir y leer */ if ((pf = fopen(j, HW+ b")) = = NULL) ( printj(HEI fichero O/OS no puede abrirse:: f); exit(l); ) printj(HFichero O/OS abierto n': f);
  • 323. while (gets(bujjer) != NULL) ( jputs(bujjer, pf); if (ferror(pf)) ( perror("Linea demasiado larga"); exit(2); l jputct n~ pj); l printjt'Introducir datos. Finalizar cada linea con Enter n"); printj("La entrada de datos jinalizard con etrl +Z n n"); / * Visualizar el contenido del jichero */ rewind(pj); / * situarse al principio del jichero */ / * leer hasta un ' n' 0 hasta N-l caracteres */ while (fgets(bujjer, N, pj) != NULL) printf("%s'~ bujjer); if (ferror(pf)) perror("Error durante la lectura"); jclose{pj); l Este programa lee lineas de texto y las almacena en un fichero. Cada linea en el fichero, va seguida del canicter ' n~ Finalmente se visualiza el contenido del fichero creado. EI siguiente ejemplo muestra como lanzar un resultado a la impresora pa- ralelo. Concretamente este programa escribe el contenido de un fichero de texto, por la impresora. La orden de ejecucion sera de la forma: cprint nombre_fichero, donde cprint es el nombre de nuestro programa y nombre_fichero es el fichero que queremos escribir por la impresora.
  • 324. /****** Escribir el contenido de un jichero por la impresora ******/ / * CPRINT.C */ #include <stdio.h > # include <stdlib.h> #dejine N 81 main(int argc, char *Grgv[ ]) { FILE *pj; char bujjer[N]; if (argc /= 2) / * chequear el numero de argumentos */ { printf(HPormato: C> cprint nombre-Jichero. n "); exit(l); l / * Abrir el jichero indicado por argv[l] para leer */ if ((pj = jopen(argv[l], Hr")) = = NULL) { printf(HEI jichero O/OS no puede abrirse. n': argv[lJ); exit(2); l / * Escribir el contenido del jichero por la impresora */ while (fgets(bujjer, N, pj) / = NULL) jputs(bujjer, stdprn); fclose(pj); l Observar la sentenciajputs(bujjer, stdprn); para que la salida se pro- duzca par la impresara, basta especificar en la funci6n utilizada para es- cribir, que el fichero de salida es el apuntada par stdprn (impresara paralela).
  • 325. Esta funcion escribe sus argumentos (arg) en el fichero apuntado por pf, con el formato especificado. La descripcion de formato, es la misma que se especifico para printf( ) La funcion jprintf( ) devuelve el numero de caracterl2S escritos 0 un valor negativo si ocurre un error. Esta funcion lee sus argumentos (arg) del fichero apuntado por pf, con el formato especificado. La descripcion de formato es la misma que se espe- cifico para scanf( ). Cad a argumento arg, debe ser un puntero a una varia· ble en la que queremos almacenar el valor lefdo. El tipo de cad a una de estas variables debe corresponderse con la especificacion de formato indio cada para cada variable. La funcion jscanf( ) devuelve el numero de argumentos que han sido lefdos y asignados. Si el valor devuelto es un 0, significa que no se han asignado val ores; y si es un EOp, significa que se ha detectado el final del fichero. # include <stdio.h> # include <stdlib.h >
  • 326. main( ) ! char bujjer[128]; FILE *ptabla; long entl, total_ent/,' float real, total_real; int i, c = ~'; / * Abrir un jichero para leer. Si no existe se crea. */ if ((ptabla = jopen("tabla.d': "r")) /= NULL) ! / * Leer datos del jichero y totalizarlos */ printf("RESULTADOS: n n"); for (i = 0, total_entl = 0, totaLJeal = 0.0; i < 10; i+ +) ! jscanj(ptabla, "%s %c: %ld %1': bujjer, &c, &entl, &real); total_entl + = ent/,' total_real + = real; printf(" t%s %c: %71d %9.2j n': bujjer, c, entl, real); l printf(" n tTotal: % 71d %9.2j n': total_entl, total_real); l else ! / * Si el jichero no existe 10 creamos. */ if ((ptabla = jopen( "tabla.d': "w")) = = NULL) exit(I); / * Se escribe la tabla deseada en el jichero. */ for (i = 0, entl = 99999, real = 3.14; i < 10; i+ +) jprintj(ptabla, " tLinea %c: % 71d %9.2j n': c+ +, entl/= 2, real * = 2); printf("EI jichero no existia y 10 hemos creado. n "); printf(" nEjecuta de nuevo el programa. n"); l jclose(ptabla); l
  • 327. Es importante conocer c6mo la funci6njprintj( ) almacena los datos sobre el disco. Los caracteres son almacenados uno por byte y los numeros enteros y reales en lugar de ocupar 2, 4 u 8 bytes dependiendo del tipo, requieren un byte por cada digito. Por ejemplo el numero 105.56 ocuparfa 6 bytes. Cuando la cantidad de datos numericos a almacenar es grande, esta funci6n no es la id6nea, ya que se ocupa mucho espacio de disco. La solu· ci6n a este problema, la presentamos en el apartado siguiente. Esta funci6n escribe hasta c elementos de longitud n bytes, almacenados en el buffer, en el fichero apuntado por pf. La funci6njwrite() devuelve el numero de elementos actualmente es· critos, que puede ser menor que c si ocurre un error. Esta funci6n lee hasta c elementos de longitud n bytes, del fichero apunta- do por pf, y los almacena en el buffer. La funci6njread() devuelve el numero de elementos actualmente lei- dos, que puede ser menor que c si ocurre un error. Utilizar las funciones jeoj( ) 0 jerror( ) para distinguir si se ha detectado el final del fichero 0 si ha ocurrido un error. Si n 0 c son 0, jread( ) devuelve un 0 y el contenido del bujjer permanece igual.
  • 328. # include <stdio.h> # include <stdlib.h> main( ) [ typedef struct r registro; struct r [ char rejerencia[20}; long precio; ]; registro reg; int bytesreg = sizeof(reg); FILE *pj; char sprecio[10}, respuesta; 1* tipo regiC)°tro';</ 1* dejinicion de un registro d 1* tamano de un registro d 1* puntero al jichero ,J / * abrir el jichero "datos" para escribir "wb" *1 pf = jopen("datos': "wb"); system ("cls "); 1* borrar pantalla */ / * Entrada de datos */ printj ("Pulse Orl +Z para jinalizar n n "); print! ("Rejerencia: "); while (gets(reg.rejerencia) /= NULL) ( printj ("Precio: "); gets(sprecio); reg.precio = atol(sprecio); / * Escribir un registro en el jichero *1 fwrite (&reg, bytesreg, 1, pf); print! (" nRejerencia: "); ] 1* cerrar el jichero d 1* desactivar el indicador eoj de stdin d fclose(pj); clearerr(stdin); do [ print! t'iDesea visualizar el jichero? (sin) ");
  • 329. / * Leer el primer registro del jichero */ jread (&reg, bytesreg, 1, pi); while (!feoj(pj)) { printj ("Rejerencia: printj (HPrecio: %s n': reg.rejerencia); %ld n n': reg.precio); respuesta = tolower(getchar( )); jjlush(stdin); } while ((respuesta!= 's') && (respuesta!= en')); / * Salida de datos */ if (respuesta = = 's') ( system(Hcls' '); / * abrir el jichero Hdatos" para leer Hrb" */ pj = jopen(Hdatos': "rb"); / * Leer el siguiente registro del jichero */ jread (&reg, bytesreg, 1, pi); } } jclose(pi); } Este ejemplo lee registros formados por dos campos, rejerencia ypre- cio, y los almacena en un fichero llamado datos. Una vez creado el fichera, disponemos de la opci6n de visualizar el fichero, registro a registro. La funci6n jwrite( ), almacena los datos numericos en formate bina- rio. Esto quiere decir que un int ocupa 2 bytes, un long ocupa 4 bytes, un jloat ocupa 4 bytes y un double ocupa 8 bytes. No hay que confundir el formato binario empleado para almacenar un dato numerico, con el modo binario en el que se abre un fichero, 10 cual s6lo indica que se va a efectuar una conversi6n de los finales de linea y del final de fichero. Con ficheros abiertos en modo texto, pueden producirse resultados ines- perados debido a la conversi6n de ' n' en CR + LF. Par ella, es aconseja- ble trabajar en modo binario.
  • 330. CONTROLDE LA MEMORIA INTERMEDIA ASOCIADA CON UN FICHERa Esta fund6n permite al usuario controlar la memoria intermedia asignada al fichero apuntado por pf. La fund6n setbuf( ) debe ejecutarse una vez abierto el fichero y antes de cualquier operad6n de lectura 0 escritura. Si el argumento buffer es NULL, no se Ie asigna una memoria intermedia al fichero. En caso con- trario, buffer es un array de caracteres de longitud BUFSIZ, constante de- finida en stdio.h. Esta memoria intermedia sera utilizada en lugar de la asignada por defecto por el sistema. Esta funci6n permite al usuario controlar la existencia 0 no de un buffer, y el tamafio del mismo. La funci6n setvbuf() debe ejecutarse una vez abierto el fichero y antes de cualquier operaci6n de lectura 0 escritura. El argumento buffer es un array de caracteres de longitud n bytes que desempefia la funci6n de buffer 0 memoria inter media; el argumento tipo puede ser:
  • 331. La fundon setvbuf( ) devuelve un 0 si no ocurre un error; y un valor distinto de 0, en caso contrario. # include <stdio.h > # include <stdlib.h> # include <time.h > int cuenta_lineas(FILE *pf); / * funci6n prototipo */ FILE * abrir(char *); / * funci6n prototipo */ char bufl[BUFSIZ], buf2[MIBUFFER]; / * buffers para el fichero */ main(int argc, char *argv[]) { time_t t_inicial; FILE *pf; int c; if (argc != 2) /* chequear el mlmero de argumentos */ ( printf("Formato: C> nombre-programa nombre-fichero. n").' exit(l); l pf = abrir(argv[l]);
  • 332. /************************************************************ Utilizando el buffer buf1, de tamano BUFSIZ setbuf(pj; buf1); t_inicial = clock( ); c = cuenta_lineas(pf); printf("Tiempo: %5.1f tTamano del Buffer: %4d n': ((f1oat)clock( )-t_inicial)/CLK_TCK, BUFSIZ); Utilizando el buffer buf2, de tamano MIBUFFER ************************************************************/ setvbuf(pj; buf2, ~OFBF; sizeof(buf2)); t_inicial = clock( ); c = cuenta_lineas(pft printf("Tiempo: %5.1f tTamano del Buffer: %4d t mi buffer n': ((f1oat)clock( )-t_inicial)/CLK_TCK, MIBUFFER); No utilizando un buffer ***********************-************************************/ setvbuf(pj;NULL, ~ONBF; 0); t_inicial = clock( ); c = cuenta_lineas(pf); printj('Tiempo: %5.1f tTamano del Buffer: %4d n': ((f1oat)clock( )-t_inicial)/CLK_TCK, 0); printf(" nEIfichero %s tiene %d /(neas n': argv{l), c); 1 Contar /(neas en un fichero de texto **************************************************************/ int cuenta_lineas(FILE *pf) I #dejineN 81 char linea_buf{N};
  • 333. while (!feof(pj)) ( jgets(linea_but N, pf); c+ +; putchart. '); J putchart n '); jclose(pj); return c; / * lee una Hnea */ / * contar lfneas */ FILE wbrir(char 4ichero) { FILE *pt· if ((pj = jopen(fichero, "r")) = = NULL) ( print/teEI jichero %s no puede abrirse. n': jichero); exit(2); J return Pt· J Este programa imprime los tiempos empleados en realizar una opera· cion de contar lineas de un texto. Primero, asigna un buffer al fichero de tamafio fijado por el sistema; segundo, asigna un buffer al fichero de ta· mafio fijado por el usuario; y tercero, sin asignar un buffer al fichera. La prueba mas satisfactoria, es la segunda; en ella el tamafio del buffer es mayor. Esta fundon escribe en el fichero apuntado por pf, el contenido del buffer asociado al mismo. Si el fichero en lugar de estar abierto para escribir esta abierto para leer, jjlush( ) borra el contenido del buffer.
  • 334. La funci6njjlush() devuelve el valor 0 si no ocurre un error y un EOF en caso contrario. Esta funci6n crea un fichero temporal. Este fichero es automaticamente borrado cuando el programa termina normalmente, 0 cuando se borra ex- plicitamente con la funci6n rmtmp( ). El fichero temporal es abierto en modo w+b. La funci6n tmpjile( ) devue1veun puntero al fichero temporal creado, o un puntero nulo si no es posible crearlo. FILE *pj; if ((pj = tmpjile()) = = NULL) printf(HNo se puede abrir un jichero temporal"); Esta funci6n es utilizada para borrar todos los ficheros temporales crea- dos por tmpfile( ), existentes en el directorio actual.
  • 335. La funci6n rmtmp( ) devuelve el numero de ficheros cerrados y bl} rrados. Esta funci6n genera una cadena de caracteres que puede utilizarse como nombre para un fichero. La cadena generada es unica; de tal modo que no existe peligro de sobreescribir un fichero existente. Si el argumento ca· dena es NULL, el resultado es almacenado en un buffer interno. El nom· bre completo esta formado por el camino especificado por la macro P_tmpdir mas la cadena generada. Para cambiar de directorio, modificar el contenido de esta macro, la cual se encuentra definida en stdio.h. La funci6n tmpnam( ) devuelve un puntero a la cadena de caracteres generada 0 un puntero nulo si el nombre existe 0 no puede generarse. La funci6n genera nombres diferentes de al menos L_tmpnam caracteres, hasta un numero de TMP-MAX. Estas macros se encuentran definidas en stdio.h. FILE *pfl, *pf2; char *nombrel, nombre2[L_tmpnam}; / * Crear un nombre para un fie hero utilizando * un buffer interno */ if ((nombrel = tmpnam(NULL)) /= NULL) printj("%s es un nombre para un fiehero n': nombrel);
  • 336. / * Crear un nombre para un jichero utilizando * un bujjer externo */ if (tmpnam(nombre2) / = NULL) printj("%s es un nombre para un jichero n': nombre2); Esta funci6n genera una cadena de caracteres que puede utilizarse como nombre para un fichera. La cadena generada es unica. EI nombre com ple- to esta formado por el camino especificado por dir, mas el nombre delfi- chero compuesto por el prefijo pref y la cadena generada. La fund6n tempnam( ) devuelve un puntera a la cadena de caracteres generada 0 un puntero nulo si el nombre existe 0 no puede generarse. FILE *pj3; char *nombre3; / * Crea un nombre con el prejiJo "temp" y 10 coloca en el *primer directorio existente de estos tres: * 1. directorio indicado por la variable de entorno TMP * 2. C: TEMP * 3. directorio P_tmpdir (dejinido en stdio.h) */ if ((nombre3 = tempnam("C: TEMP': "temp")) /= NULL) printft'%s es un nombre para un jichero n':nombre3);
  • 337. Esta funci6n, mueve el puntero de LIE asociado con el fichero apuntado por pf, a una nueva 10calizaci6n desplazada desp bytes de la posici6n dada por el argumento pos. La funci6n jseek( ) devuelve un 0 si no se ha producido un error y un valor distinto de 0 en caso contrario. Este ejemplo situa el puntero de LIE, al principio del fichero apunta- do por pjichero. Observar que el desplazamiento es 0 y esta expresado como un valor long (L). Para ficheros abiertos en modo texto, jseek( ) puede producir un re- sultado inesperado debido a la conversi6n de ' n' en CR + LF. Por 10 tanto en modo texto, las operaciones con la funci6n jseek( ) seran buenas con un desplazamiento 0 (ir al principio 0 al final) 0 can un desplazamiento devuelto par la funci6n jtell( ), a partir del camienzo del fichero.
  • 338. Con el fin de no obtener resultados inesperados, es aconsejable traba- jar en modo binario. Esta funci6n da como resultado la posici6n actual del puntero de LIE, dentro del fichero apuntado por pf. Esta posici6n es relativa al principio del fichero. La funci6n jtell( ) devuelve la posici6n actual del puntero de LIE, 0 el valor -lL si ocurre un error. long pas; pas = jtell(pf); Este ejemplo almacena en la variable pas, la posici6n actual del pun- tero de LIE del fichero apuntado por pf Esta funci6n pone el puntero de LIE del fichero apuntado por pf, al co- mienzo del mismo. con la excepci6n de que rewind( ) desactiva los indicadores de error y de fin de fichero, y jseek( ) no.
  • 339. I* variable de tipo registro* 1* tamaflO de un registro* 1* puntero al jichero , 1* nO total de registros, 1* nOde registro, 1* desplazamiento en bytes* # include <stdio.h> # include <stdlib.h > main( ) [ typedef struct r registro; struct r [ char rejerencia[20}; long precio; ]; registro reg; int bytesreg sizeof(reg); FILE *pj; int totalreg; int nreg; long desp; int c, respuesta; char sprecio[IO}; 1* tipo registro, 1* dejinicion de un registro* 1* abrir el jichero "datos" para leer y escribir "r+b" *1 pj = jopen("datos'; "r+ b"); 1* Calcular el n° total de registros del jichero *1 jseek(pj, OL, SEEK--END); totalreg = jtell(pj)lbytesreg; 1* Presentar un registro en pantalla *1 do [ system("cls"); 1* borrar panta/la '! printjt'N° registro entre 1 y %d (0 para salir): ';totalreg); c = scanj("%d'; &nreg); jjlush(stdin);
  • 340. if (c && (nreg > = I) && (nreg < = totalreg)) [ desp = (long)(nreg - I) * bytesreg; jseek(pf, desp, SEEK_SET); jread(&reg, bytesreg, I, pf); printf(H nRejerencia: %s n': reg.rejerencia); printf(rcprecio: %ld n n': reg.precio); 1* Modificar el registro seleccionado *1 do [ printj (''(,Desea modificar este registro? (sin) "); respuesta = tolower(getchar( )); jjlush(stdin); J while ((respuesta /= es') && (respuesta /= en')); if (respuesta = = es') [ printj (rc nRejerencia: "); gets(reg.rejerencia); printj (rcprecio: "); gets(sprecio); reg.precio = atol(sprecio); 1* Escribir un registro en el jichero */ jseek(pf, -bytesreg, SEEK_CUR); jwrite (&reg, bytesreg, I, pj); J J 1 while (nreg); jclose(pf); 1 Este programa ilustra el uso de las funcionesjseek(), jtell() y rewind( ). El programa abre un fichero Hamado datos con registros de tipo registro, calcula el numero de registros del fichero, presenta en pantaHa un determi- nado registro previamente seleccionado, y pregunta si se desea modificar.
  • 341. Esta funci6n pone el puntero de LIE correspondiente al fichero apuntado por pf, en la posici6n indicada por pos; generalmente, este valor habra side obtenido previamente por la funci6njgetpos( ); aunque tambien puede ser un valor calculado. La ejecuci6n de esta funci6n, desactiva el indicador de fin de fichero y anula el efecto de cualquier funci6n ungetc( ) previa. La funci6n jsetpos( ) devuelve un 0 si no ocurre un error y un valor distinto de 0, en caso contrario. long desp; desp = (Iong)((numreg - 1) * bytesreg; jseek(pf, desp, SEEK_SET); jpos_t desp; desp = (fpos_t)((numreg - 1) * bytesreg; jsetpos( pf, &desp); Esta funci6n almacena en el objeto apuntado por pos, la posici6n del pun- tero de LIE del fichero apuntado por pf. Posteriormente podremos utili- zar la funci6n jsetpos( ) para restaurar la posici6n de este puntero.
  • 342. La funcion fgetpos( ) devue1veun 0 si no ocurre un error y un valor distinto de 0, en caso contrario. Basandonos en el fichero datos del ejemplo anterior, desarrollar una funcion buscar( ) que nos devuelva como resultado el numero del registro para un dato referencia (ref) dado. int buscar(FILE *pf, char *ref) I jpos_t desp; / * Situarse al principio del fichero *1 desp = (fpos_t)O; jsetpos( pf, &desp); do I jread(&reg, bytesreg, 1, pf); I while (strncmp(reg.referencia, ref, 4)); jgetpos(pf, &desp); printf( " nRegistro %d n n': desplbytesreg ); printj ("Referencia: %s n': reg.referencia); printj (<<Frecio: %ld n n': reg.precio); return(desplbytesreg); I Esta funcion abre e1fichero especificado por path y permite compartirlo por otros procesos. El argumento acceso especifica como es abierto el fi- cheroy el argumento sh es una constante que indica los atributos para com- partir el fichero.
  • 343. # include <stdio.h > # include <share.h > acceso: r, w, a, r+, w+, a+ Para indicar d tipo de conversion, pueden aiiadirse 10s caracteres to b. sh: SH_COMPAT, SH~ENYNO, SH~ENYRD, SH~ENYRW, SH~ENYWR La funcion -fsopen( ) devuelve un puntero a una estructura de tipo FILE, la cual se corresponde con el fichero abierto. Un puntero nul0 indio ca un error. Si SHARE.COM (0 SHARE.EXE) no esta instal ado, MS-DOS igno· ra el modo compartido.
  • 344. Las funciones de E/S de bajo nivel, denominadas en C "Low-level I/O func- tions", ofrecen po cas formas para manipular los datos. No utilizan una memoria intermedia (buffer) para las operaciones de entrada salida, y tam- poco pueden dar formato a los datos. Las declaraciones para estas funcio- nes, se dan en los ficheros: io.h, sys/types.h, sys/stat.h, fcntl.h, y errno.h. Las funciones que vamos a ver en este capitulo no pertenecen al C estan- dar (ANSI). Las funciones .de E/S de bajo nivel son, generalmente, incompatibles con las funciones de E/S estandar. Las funciones de E/S estandar pueden pro- cesar ficheros que tengan, 0 no, asociado un buffer; mientras que las fun- ciones de E/S debajo nivel s610 pueden procesar ficheros que no tengan asociado un buffer. Si durante el proceso de un fichero utilizamos ambos tipos de funciones, el fichero no debe tener asociado un buffer. Cuando un fichero no tiene asociado un buffer, cada byte escrito a, 0 leido desde el fichero, es fisicamente transferido en el momenta de la operaci6n.
  • 345. stdin stdout stderr stdaux stdprn dispositivo de entrada estandar (teclado) dispositivo de salida estandar (pantalla) dispositivo de error estandar (pantalla) dispositivo auxiliar estandar (puerto serie) dispositivo de impresi6n estandar (impresora paralelo) Para poder escribir 0 leer sobre un fichero, primeramente hay que abrirlo con las funciones open ( ), sopen( ) 0 creat( ). El fichero puede ser abierto para leer, para escribir 0 para leer y escribir y puede ser abierto en modo texto 0 en modo binario. Estas funciones devuelven un mimero denominado descriptor de fi- chero, que sera utilizado mas tarde para referirse al fichero en las opera- ciones de lectura/escritura. El descriptor asignado a una determinada stream, puede saberse por medio de la funci6n jileno( ). Cuando un programa comienza su ejecuci6n, cinco ficheros, que se corresponden con dispositivos, son abiertos automaticamente. Estos ficheros, direccionados por streams, y los dispositivos asociados por defecto son: De estos cinco, dos de ellos, el dispositivo serie y el dispositivo de im- presi6n paralelo, dependen de la configuraci6n de la maquina, por 10 tan- to pueden no estar presentes. Despues de haber finalizado el trabajo con un fichero, hay que cerrarlo con la funci6n close(). Si un fichero no se cierra explicitamente, es cerrado automaticamente cuando finaliza el programa. Sin embargo, es aconseja- ble cerrar un fichero cuando se ha finalizado con el, ya que el numero de ficheros abiertos al mismo tiempo esta limitado.
  • 346. Las operaciones de lectura y escritura se realizan por medio de las funcio- nes read() y write(). Siempre empiezan en la posicion sefialada por el de- nominado puntero de lectura escritura (LIE). La posicion de LIE puede ser situada en cualquier parte del fichero, utilizando la funcion lseek(). Para determinar en que posicion nos encon- tramos tenemos la funcion leU( ). Esta funcion abre el fichero especificado por path para el tipo de opera- cion indicado por modo. #include <fcntl.h > # include <io.h > # include <sys types.h > # include <sys stat.h > # include <errno.h > El argumento modo es una expresion entera formada por una 0 mas constantes separadas por el operador OR (I). Estas constantes definidas en fcntl.h son las siguientes: Abre un fichero para afiadir informacion al final del mismo.
  • 347. Crea un nuevo fichero para escribir. Si el fichero exis- te, esta con stante no tiene efecto. Devuelve un numero de error (variable errno) si el fichero especificado existe. Solamente se utiliza con O_CREAT. Abre un fichero s610 para leer. Esta constante no se puede utilizar con las constantes: O_RDWR 0 O_WRONLY. Abre un fichero para leer y escribir. Esta constante no se puede utilizar con las constantes: O~DONLY 0 O_WRONLY. Abre un fichero existente, destruyendo su conteni- do. El fichero debe tener permiso de escritura. Abre un fichero solo para escribir. Si el fichero no existe, no se crea y se produce un error. Esta cons- tante no se puede utilizar con las constantes: O_RDONLY 0 O_RDWR. Para indicar el modo de acceso, debe especificarse al menos una de estas constantes: O_RDONLY, 0_ WRONLY, 0 O_RDWR. El argumento pmodo solamente es necesario con la constante O_CREAT, para indicar el permiso dado al fichero. Si el fichero existe, este argumento es ignorado.
  • 348. Estas constantes estan definidas en el fichero sys stat.h. Este fiche- ra, debe incluirse despues del fichero sys types.h; ya que las declaracio- nes que conti ene, necesitan de los tipos definidos en este ultimo. No es posible asignar bajo DOS a un fichero, el atributo de s610escri- tura. Comos todos los ficheros tienen permiso de lectura, los permisos S_IREAD I S_IWRITE y S_IWRITE son equivalentes. Bajo las versiones de DOS 3.0 0 posteriores y con la orden SHARE instalada, hay problemas si se abre un fichero en el modo O_CREAT I O-l{DONLY 0 en el modo O_CREAT I 0_WRONLY y con un valor S_IREAD para pmodo. La soluci6n al problema que surge (se cierra pre- maturamente el fichero), es abrir el fichero en el modo O_CREAT I O-l{DWR. La funci6n open ( ) retorna un numero (descriptor de fichero) que ha sido asociado al fichero abierto, para dirigirse a el en posteriores operacio- nes de lectura 0 escritura. Un valor -1 indica un error y la variable errno es puesta a uno de los siguientes valores: EACCES EEXIST EMFILE ENOENT EINVAL Permiso denegado Fichero existe. O_CREAT y O~XCL son especificados Demasiados ficheros abiertos No existe tal fichero 0 directorio Argumento invalido
  • 349. El numero devuelto por la fund6n open( ), se corresponde con eli dice del elemento del array de estructuras _iob[ J, utilizado para el fich ro abierto (ver funci6n fopen( ). # include <fcntl.h> # include <sys types.h > # include <sys stat.h > # include < io.h > # include <stdio.h > main( ) ( int nfl, nf2,' nf1 = open(Hdatosl': O.-RDONLY); if (nf1 = = -1) perror(Herror al abrir el ficheroH ); else printftjichero abierto para leer n"); nf2 = open("datos2': OJDWR I O_CREAT, S~READ I S~WRITEt if (nf2 = = -1) perror(Herror al abrir el ficheroH ); else printftjichero abierto para leer y escribir nH ); Este programa utiliza la fund6n open (), para abrir un fichero llama do datos1 para leer, y un fichero llamado datos2 para leer y escribir. Si d fichero datos2 existe, se abre y si no se crea; en este ultimo caso, IeSOD asignados los permisos especificados (lectura y escritura). Si el fichero exist~ los permisos son ignorados. Esta fund6n abre un fichero especificado por path para escribir. Si elfi· chero existe y tiene permiso de escritura, destruye su contenido. El argu·
  • 350. mento pmodo, se aplica a ficheros de nueva creaci6n solamente. Su signifi- cado es el mismo que se ha expuesto en la funci6n open( ). # include <sys types.h > # include <sy stat.h > # include <io.h > # include <errno.h > La funci6n creat( ) retorna un numero que ha side asociado al fichero creado, para dirigirse a el en posteriores operaciones de lectura 0 escritura. Un valor -1 indica un error y la variable errno es puesta al valor correspon- diente. nf == creat(Hdatos': S~READ I S~WRITE); if (nf == == -1) perror(Herror al crear el jichero"); else printj(Hjichero creado n"); Esta funci6n indica si se ha llegado al final del fichero cuyo numero aso- ciado es num. #include < io.h > # include <errno.h > La fund6n eo/( ) devuelve el valor 1 si se ha llegado al final del fiche- ro; en caso contrario, devuelve el valor O. Un valor -I indica un error y la variableerrno es puesta al valor correspondiente.
  • 351. / * Leer hasta encontrar el fin de fichero */ while (!eof(nf)) [ / * Leer 80 0 menos bytes */ if ((nbytes = read(nj, buffer, 80)) - - -1) [ perror(HError de lectura"); break; } total + = nbytes; / * bytes lefdos */ } printf(HNumero de bytes lefdos: %d n': total); En este ejemplo, se Ieen datos del fichero referenciado por nf EI pro- ceso finaliza cuando se a1canza el final del fichero 0 si ocurre un error du- rante la lectura. EI resultado es el numero total de bytes leidos. int nf; nf = fileno(stdprn); Este ejemplo asigna a nj, el numero asociado al fichero stdprn (im- presora paralelo).
  • 352. Esta funci6n cierra el fichero que tenga como descriptor num, liberando as! dicho descriptor 10 que permitini que sea utilizado por otro fichero. # include <io.h > # include <errno.h > La funci6n closer ) devuelve un 0 si el fichero es cerrado. Un valor -1 indica un error y la variable errno es puesta al valor correspondiente. Esta funci6n escribe c bytes, almacenados en buffer, en el fichero asociado con num. # include <io.h > # include <errno.h > La funci6n writer ) retorna el numero de bytes actualmente escritos. Un valor -1 indica un error y la variable errno es puesta al valor correspon- diente. Esta funci6n intenta leer c bytes, del fichero cuyo descriptor asociado es num, y los almacena en buffer.
  • 353. # include <io.h > # include <errno.h > La funci6n read( ) retorna el numero de bytes actual mente leidos. Un valor de 0 indica que se ha encontrado un fin de fichero. Un valor -1 indica un error y la variable errno es puesta al valor correspondiente. EI siguiente programa, recibe como panimetros el nombre de dos fi- cheros y copia el contenido del primero sobre el segundo. Si el fichero des- tino existe, se nos preguntani si queremos sobreescribirlo. Cuando el pro- grama finaliza, presenta un mensaje para saber si la copia se ha hecho satisfactoriamente, 0 si ha ocurrido un error. / * Funciones de bajo nivel (low-level junctions I/O). * Reproducci6n de la orden copy. * CCOPY.C */ # include <stdio.h > # include <io.h > # include <malloc.h > # include <errno.h > # include <conio.h > # include <jcntl.h> # include <sys types.h > # include <sys stat.h > int copiar-f(char *, char *); / * dejiniciones de constantes. S_ */ / * prototipo */ main(int argc, char ~rgv[ J) / * junci6n principal */ [ if (argc = = 3) if (copiar-f(argv[lJ, argv[2J)) printj(HFallo en la copia n "); else printj(HCopia ejectuada n"); else printj(H FORMATO: CCOPY juente destino n");
  • 354. / * Copiar un fichero en otro.. 81 tamano del buffer se asigna * dindmicamente. Avisa si el fichero existe. Si la copia se * efectua satisfactoriamente devuelve un 0, en caso contrario *-devuelve un numero de error (errno). */ int copiar-f(char 4uente, char ~estino) [ char *buf; int n-fuente, n_destino, car; unsigned bytes = 2048; 1* Abrir el fichero fuente y crear el fichero destino, * sobreescribiendo si es necesario. *1 if ((n-fuente = open(juente, O---.BINARY I O---.RDONLY)) = = - 1) return errno; n_destino = open(destino, O---.BINARY I O_WRONLY I O_CREAT I O--.EXCL, S-lREAD I S-lWRITE); if (errno = = EEXIST) 1* fichero existe *1 [ printj(HEI fichero destino existe. ;, Sobreescribir ? (sin) "); car = tolower(getch( )); if (car = = 's') n_destino = open(destin0, OJINARY I O_WRONLY I O_CREAT I O_TRUNC, S-.JREAD I S-.JWRITE); printj( H n" ); 1 if (n_destino = = -1) return errno; / * Asignacion dindmica de memoria para el buffer *1 if ((buf = (char *)malloc((size_t)bytes)) = = NULL) return ENOMEM; 1* insuficiente memoria *1 / * Leer del fichero fuente y escribir en el destino *1 while (!eof(n-fuente)) [ if ((bytes = read(n-fuente, buj, bytes)) = = -1) return errno; if ((bytes = write(n_destino, buj, bytes)) = = - 1)
  • 355. J / * Cerrar jicheros y liberar memoria d close(n-Juente); close(n_destino ); jree(buj); return 0; Esta funci6n mueve el puntero de LIE asociado con el fichero abierto con el numero num, a una nueva localizaci6n desplazada desp bytes de pos. # include <io.h > # include < stdio.h > # include <errno.h > La funci6n lseek( ) devuelve el desplazamiento en bytes de la nueva posici6n relativa al principio del fichero. Un valor -IL indica un error) la variable errno es puesta al valor correspondiente.
  • 356. Esta funcion da como resultado la posicion actual del puntero de LIE, del fichero asociado con num. Esta posicion es relativa al principio del fichero. #include <io.h > # include <errno.h > La funcion teU( ) devuelve e1desplazamiento en bytes de la posicion actual relativa al principio del fichero. Un valor -IL indica un error y la variable errno es puesta al valor correspondiente. EI siguiente programa, solicita de la entrada estandar, el nombre de un fichero y un caracter. EI resultado que se busca es la posicion del pri- mer caracter, existente dentro del fichero, igual al dado y e1numero total de bytes de dicho fichero. # include < stdio.h > # include <stdlib.h> # include <io.h > # include <jcntl.h> main( ) I int num, carb; unsigned bytes; long desp, longit; char car, nombre-J[31J;
  • 357. do ( printj(Hlntroducir el nombre del jichero: "),o gets(nom bre-f),o num = open(nombre-J, O-fiINARY I O-RDONLY),o } while (num = = -1); / * Buscar un cardcter especijicado */ lseek(num, OL, SEEK--..SET),o / *principio del jichero */ printj(HEscribir el cardcter a buscar: "),o carb = getchar( ),o / * Leer hasta encontrar el cardcter buscado */ do { / * bytes = bytes lefdos */ if ((bytes = read(num, &car, 1)) -1) error(HError de lectura"),o } while ((car /= (char)carb) && bytes); / * Escribir el resultado obtenido */ desp = tell(num),o if (bytes) / * si se ha encontrado */ printjt' nEI primer ASCII %u t%c') estd en el byte %ld n': carb, carb, desp),o else printj(H nEI ASCII %u t%c') no se encuentra n': carb, carb); / * Longitud del jichero */ desp = lseek(num, OL, SEEK---.END),o printj(" nEI jichero tiene un total de %d bytes n': desp),o close(num),o . } void error(char *mens_error) ( perror(mens_error ),o exit(l),o }
  • 358. Esta funci6n asocia un segundo descriptor, al fichero asociado con e1des- criptor num. # include <io.h > # include <errno.h > La funci6n dup( ) devuelve un nuevo descriptor si se ejecuta satisfac- toriamente. Un valor -1 indica un error y la variable errno es puesta al va- lor correspondiente. Esta funci6n asocia un segundo descriptor num2, al fichero asociado con el descriptor numl. Si ya existe un fichero abierto con el descriptor num2, este es cerrado. # include <io.h > # include < errno.h > La funci6n dup2() devuelve un 0 si se ejecuta satisfactoriamente. Un valor-1 indica un error y la variable errno es puesta al valor correspondiente. Esta funci6n abre el fichero especificado por path para el tipo de opera- cionindicado por modo, y permite que sea compartido por otros procesos. #include < fcntI.h > # include < io.h > # include <share.h > # include <sys types.h >
  • 359. # include <sys stat.h > # include <errno.h > O~PPEND, O_BINARY, O_CREAT, O-.-EXCL, O_RDONLY O_RDWR, O_TEXT, O_TRUNC, O_WRONLY (Ver open( )) SH_C OM PAT, SH_DENYNO, SH_DENYRD, SH~ENYRW, SH_DENYWR (Ver -Jsopen( )) S_IWRITE, S_IREAD, S_IREAD I S_IWRITE (Ver open( )) La fundon sopen() retorna un numero (descriptor de fichero) que ha sido asociado al fichero abierto, para dirigirse a el en posteriores operacio- nes de lectura 0 escritura. Un valor -1 indica un error y la variable errno es puesta al valor correspondiente. Si SHARE.COM (0 SHARE.EXE) no esta instalado, MS-DOS igno- ra el modo compartido. Esta funcion pone la mascara de permisos del proceso actual, al modo es- pecificado por pmodo. Esta mascara es utilizada para modificar los per- misos asignados a los nuevos ficheros que se creen por medio de las fun- dones creat(), open( ) y sopen(). Si en la mascara un bit esta a 1, el correspondiente bit en el permiso asignado al fichero es puesto a O. Si es 0, no hay cambios. # include <sys types.h > # include <sys stat.h > # include <io.h >
  • 360. Bajo MS-DOS todos los ficheros tienen permiso de lectura; no es po- sible, asignarles permiso de escritura solamente. Por ejemplo, si el bit de escritura es puesto a 1 en la mascara, cual- quier nuevo fichero creado seria de s610 lectura. Este ejemplo pone el permiso de s610 lectura, a todos los futuros fi- cheros que se creen por medio de las funciones creat( ), open ( ) y sopen( ).
  • 361. FUNCIONES PARA LA CONSOLA Y LOS PUERTOS DE E/S Lasfunciones para la consola y los puertos de E/S, ejecutan las operacio- nesdelectura y escritura sobre la consola (teclado/pantalla) 0 sobre un puer- to especifico. La salida y la entrada de estas funciones pueden ser redirec- cionadas. La redirecci6n se efectua a nivel de sistema operativo. Estasfunciones son especificas de MS-DOS y OS/2. Las funciones utiliza- daspara la entrada de datos, leen los mismos de la entrada estandar (stdin) y las funciones utilizadas para la salida de datos, escriben los mismos so- brela salida estandar (stdout). Las declaraciones para todas estas funcio- nes, estan contenidas en el fichero conio.h. Leeun caracter del teclado sin que se produzca eco (el caracter no se vi- sualizaen pantalla). No es necesario pulsar Enter. No hay conversi6n de caracteres: CR+LF no es convertido a f n:
  • 362. La funcion getch( ) devuelve el canicter leido. Si este valor es cera, !Ia· mar otra vez a la funcion para recuperar el segundo codigo (ver Codigo Extendidos en los apendices). Lee un canicter del teclado, produciendose eco (el caracter se visualiza en pantalla). No es necesario pulsar Enter. No hay conversion de caracteres: CR + LF no es convertido a ' n: La funcion getche( ) devuelve el caracter leido. Si este valor es cero, llamar otra vez a la funcion para recuperar el segundo codigo (ver Codigos Extendidos en los apendices). Escribe directamente en la consola, el caracter c. No hay conversion de ca· racteres: ' n' no es convertido a CR + LF. La funcion putch( ) retorna el caracter c si se ejecuta satisfactoriamente y un valor EOF si ocurre un error.
  • 363. Esta funcion chequea el teclado, buscando el canicter correspondiente a la tecla mas recientemente pulsada. La funcion kbhit( ) devuelve un valor distinto de 0 si se ha puisado una tecla, si no devuelve un valor O. printf(HPulse una tecla para continuar "); while (!kbhit( )); Esta funcion devuelve a la memoria intermedia de la consola, el ultimo canicter leido, haciendo que este caracter sea el proximo caracter a leer. La funcion ungetch( ) devuelve el caracter car si existe. Si el valor de- vuelto es EOp, entonces ha ocurrido un error. # include <stdio.h> # include < conio.h > # include <ctype.h > main( ) { char car; iot n;
  • 364. putst'Datos ..."); car = getche( ); while (lisdigit(car)) car = getche( ); car = ungetch(car); cscanf("%d': &n); printf(" n%d n': n*lOO); } Datos ... boligrafos 25 Este ejemplo lee caracteres hasta leer un digito. EI digito leido esde· vuelto al buffer por la funci6n ungetch( ), para que a continuaci6n la fun· ci6n cscanf( ) lea el valor tecleado. Lee directamente de la consola una cadena de caracteres, hasta encontrar el canicter ' n', y almacena en buffer la cadena y su longitud, de la forma siguiente: buffer[O] contiene el maximo numero de caracteres que pueden leerse,in· cluyendo el canicter nulo de terminaci6n. buffer[l] contiene el numero de caracteres leidos, excluyendo el canicter nulo de terminaci6n. La funci6n cgets( ) retorna un puntero al comienzo de la cadena de caracteres (direcci6n de bujjer[2]).
  • 365. la funcion cputs( ) retorna un 0 si se ejecuta satisfactoriamente y un valor distinto de 0 si ocurre un error. Leedatos directamente de la consola, los interpreta de acuerdo con el for- mato indicado y los almacena en los argumentos especificados. No hay con- version de caracteres: CR + LF no es convertido a ' n'. Cuando se leen varios datos consecutivos, no hay que poner especial cuidado en eliminar el canicter LF, que normalmente queda en el buffer del teclado. La funcion cscanf( ) retorna un entero, correspondiente al numero de datos leidos y asignados, 0 un EOP cuando se intenta leer un end-of-file. Escribedirectamente en la consola datos con formato. No hay conversion de caracteres: ' n' no es convertido a CR + LF.
  • 366. # include <conio.h > # define N 20 int main(void) { char buffer[NJ int n; printf(<tlntroducir una cadena (%d caracteres max): ': N-l); cadena = cgets(buffer); printj(<t nMdximo nOde caracteres: %d n': buffer[O]); printj(<tN° de caracteres actuales: %d n': buffer[l]); cputs( cadena); putch( n'); putch( r'); cprintj(<tlntroducir el nOde elementos: "); cscanf(<t%d': &n); cprintj(<t nTipo de elementos: "); gets(cadena); cprintj(<t nHay %d %s n r': n, cadena); Observar la utilizacion del canicter ' r' debido a que no hay conver· sion (' n' a CR +LF). Observar tambien, que despues de cscanf( ) no es necesario limpiar el buffer del teclado; al no quedar en el mismo el canic· ter LF, gets( ) se ha ejecutado normalmente. Con respecto al teclado, sabemos que al pulsar una tecla se genera un byte, correspondiente al codigo ASCII del canicter (ver codigo de caracteres de ASCII, en los apendices).
  • 367. char byte; byte = getch( ); printf("%d': byte); Este ejemplo da como resultado el valor ASCII de la tecla pulsada. Par ejemplo, si pulsamos la tecla 'a', se escribe 97. Sin embargo, existen muchas teclas y combinaciones de teclas no re- presentadas por un unico byte. Por ejemplo las teclas FI a Fl6 0 la combi- naci6n de teclas AU+A (ver c6digos extendidos en los apendices). El c6di- go de estas consta de dos bytes. El primer byte siempre vale O. char byte], byte2; byte] = getch( ); byte2 = getch( ); printf("%d O/Od': byte], byte2); Este ejemplo da como resultado el c6digo extendido correspondiente a estetipo de teclas. Por ejemplo si pulsamos FI, se escribe 0 59, si puIsa- mas AU+A se escribe 0 30. Observar que tenemos que emplear dos veces la funci6n getch( ). El sistema operativo MS-DOS dispone de un sistema para controlar directamente el cursor sobre la pantalla. Este sistema no esta implementa- do sabre la memoria ROM como las rutinas norm ales de acceso a disposi- tivos de E/S, sino que esta contenirn un fichero separado llamado ANSI.SYS. Para poder utilizar este fichero, debe ser previamente instalado en el sistemaoperativo. Para ello incluir en el fichero del sistema CONFIG.SYS la orden: DEVICE = ANSI.SYS. ANSI.SYS, para controlar el cursor, interpreta secuencias de escape seguidasde uno 0 mas caracteres especiales.
  • 368. Los caracteres especiales que podemos poner a continuaci6n, son las siguientes: 2J K A B C D 010d; 010df s u O1odA 010dB O1odC O1odD O1odm O1od;010dm Borra la pantalla. Borra la linea donde esta el cursor. Mueve el cursor una fila hacia arriba. Mueve el cursor una fila hacia abajo. Mueve el cursor una columna a la derecha. Mueve el cursor una columna a la izquierda. Mueve el cursor a la fila y columna especificadas. Memoriza la posici6n del cursor. Restaura la posici6n. Mueve el cursor O1od filas hacia arriba. Mueve el cursor O1od filas hacia abajo. Mueve el cursor O1od columnas hacia la derecha. Mueve el cursor O1od columnas hacia la izquierda. Cambiar el atributo de un caracter. Color del primer plano y color de fondo. Cada caracter visualizado sobvera pantalla, es almacenado en memo- ria con dos bytes. Un byte contiene el c6digo normal del caracter y el otra contiene el atributo del caracter. Volver a modo normal (blanco sobre negro). Caracteres en alta intensidad. Caracteres subrayados. Caracteres parpadeando. Caracteres en video inverso. Caracteres no visibles.
  • 369. Los colores para el primer plano y para el fonda se pueden elegir de entre los presentados en la tabla siguiente: Negro Rojo Verde Amarillo Azul Magenta Cyan Blanco Estas secuencias de caracteres pueden ser transmitidas por la funci6n printf( }. printj(" x1B[2J"}; / * borra la pantalla d / * situar el cursor en la fila 10, columna 1 */ printj(', x1B[%d;%dj': 10, 1); printf(" x1B[7m "}; ) / * escribir en video invertido */ printf(" x1B[Om"}; / * vuelta a normal */ / * Presentaci6n en pantalla, en verde con fondo azul */ printf(" x1B[%d;%dm': 32, 44); printf(" x1B[Om"}; / * vuelta a normal */ El siguiente ejemplo muestra un programa que presenta un menu en pantalla. Cada opci6n de este menu se elige situandonos sobre ella con las teclasde movimiento del cursor (arriba, abajo). La opci6n elegida siempre esta en video inverso. Para ejecutarla se pulsa la tecla Enter. # include < stdio.h > # include < stdlib.h >
  • 370. # dejine VERDAD 1 #dejine NUM 7 # dejine BPAN " x1B[2J" #dejine BLIN "x1B[K" #dejine INVER" x1B[7m" #dejine NORMAL "x1B[Om" #dejine LIN_U 72 #dejine LIN----.D 80 #dejine ENTER 13 / * numero de lfneas del menu */ / * borrar pantalla */ / * borrar lfnea */ / * vfdeo inverso */ / * vuelta a normal */ / * cursor una lfnea hacia arriba */ / * cursor una lfnea hacia abajo d / * tecla Enter */ / * Cursor a la posicion x, y */ #dejine POS_C(x, y) printjt'x1B[%d;%dj': (x), (y)) main() { void menu(char *[J, int, int); int getcodigo(void); void ejecutar(int); static char *linea[NUMJ { printf(BPAN); while (VERDAD) { menu(linea, NUM, pos_cursor); codigo = getcodigo( ); switch (codigo) { case LIN_U "Altas': "Bajas': "Modijicaciones': "Visualizacion de un registro': "Listado por pantalla': "Listado por impresora': "Salir" }; int pos_cursor = 0; int codigo; / * cursor en la prim era lfnea del menu */ / * codigo de la tecla pulsada *1 / * visualizar menu *, / * explorar teclado *,
  • 371. if (pos_cursor > 0) --pos_cursor; else pos_cursor = NUM-1; break; case LIN--.D: if (pos_cursor < NUM-1) + +pos_cursor; else pos_cursor = 0; break; case ENTER: ejecutar(pos_cursor ); break; 1 1 1 /*******************'(unci6n visualizar menu *******************/ void menu(char *linea[J, int num, int pos) [ int f; for if = 0;f < num; f + +) [ POS_C(10+ f, 25); if if = = pos) printj(H%s%s%s n': INVER, *(linea+f), NORMAL); else printj(H%s n': *(linea+f)); /******************* Funci6n explorar teclado ******************* int getcodigo(void) [ int tecla; while ((tecla = getch( )) /= 0) if (tecla = = 13) return(tecla); return(getch( )); 1 / * esperar por un 0 inicial */ / * se ha pulsado Enter */ / * devolver segundo c6digo */
  • 372. 1******************* Funci6n ejecutar opci6n *******************/ void ejecutar(int pos) ( printj(BLIN); switch(pos) ( case 0: printj(<tAltas"); break; case 1: printj(<tBajas"); break; case 2: printjt(Modijicaciones"); break; case 3: printf(<tVisualizaci6n de un registro"); break; case 4: printjt(Listado por pantalla"); break; case 5: printj(<tListado por impresora"); break; case 6: exit(O); } } I * Las Ifneas printj de esta sentencia switch, estdn sustituyendo * a 10 que en la realidad sedan llamadas a rutinas *1 Un puerto de E/S es una zona fisica utilizada como medio de comunica- ci6n entre la unidad central de proceso y un dispositivo periferico. Los puer· tos se identifican por direcciones especiales de E/S de 16 bits. Para trans- mitir un dato, primero se envia su direcci6n de destino (puerto) por el bus de direcciones, y a continuaci6n se envia el dato por el bus de datos. Estas direcciones no tienen nada que ver con las direcciones de la memoria RAM. Su utilizaci6n se requiere normalmente, cuando los servicios de interrup· ciones del BIOS (Basic Input/Output System) y del DOS (Disk Operating System) no proveen soporte para un dispositivo periferico; por ejemplo, si queremos enviar un dato al altavoz, tenemos que hacerlo a traves desu puerto de E/S. El BIOS provee el acceso a los dispositivos perifericos es-
  • 373. tandar tales como el teclado, pantalla, unidad de disco flexible, unidad de disco duro, impresora, reloj, y joystick; mientras que el DOS provee servi- cios adicionales que suplementan al BIOS. Cada dispositivo periferico de un ordenador esta asignado a un rango de direcciones de memoria de E/S las cuales pueden variar de unos orde- nadores a otros. Como ejemplo, citamos a continuacion algunos puertos de uso comun en ordenadores personales. Ox040-0x05F Ox060-0x06F OxlFO-OxlF8 Ox3BC-Ox3BF Ox3DO-Ox3Df Ox3FO-Ox3F7 Ox3F8-0x3FF Timer (8254.2) Teclado (8042) Controlador de disco duro Puertotaralelo 1 (impresora paralelo) Tarjeta"GA 0 MCGA 0 VGA emulando CGA Controlador de disco flexible Puerto serie 1 Esta funcion lee un byte del puerto de entrada especificado. EI valor del argumento puede ser cualquier entero sin signo, en el rango 0 a 65535 (OxOOOO a OxFFFF). Esta funcion escribe un byte en el puerto de salida especificado. EI valor de puerto puede ser cualquier entero sin signo, en el rango 0 a 65535; byte puede ser cualquier entero, en el rango de 0 a 255.
  • 374. Esta funci6n lee una palabra de dos bytes del puerto de entrada especifica· do. EI valor del argumento puede ser cualquier entero sin signa, en elran· go 0 a 65535. Esta funci6n escribe una palabra de dos bytes en el puerto de salida esp ficado. EI valor de puerto puede ser cualquier entero sin signo, en el rang o a 65535; palabra puede ser cualquier entero, en el rango de 0 a 65535 EI siguiente programa emite por el altavoz un pitido de una deter nada frecuencia y duraci6n. EI metoda consiste en utilizar el temporizador programable inte del PC (timer) para enviar al altavoz una frecuencia determinada. Elti (8254) obtiene una sefial proporcionada por el reloj principal del PC, q oscila a una frecuencia de 1193180Hz. Para producir un sonido con elte porizador primero, hay que programar el temporizador para generar frecuencia; a continuaci6n, se debe de dirigir la salida del temporiza
  • 375. al altavoz. El sonido se producini indefinidamente, a menos que se desac- tive el altavoz. EI temporizador se programa proporciomindole un numero. Cuando recibe una orden de dirigir su salida al altavoz, cuenta pulsos de reloj del sistema (oscila a 1193180Hz) hasta alcanzar el valor proporcionado. En:- tonces se produce un pulse y comienza la cuenta otra vez desde cero. De acuerdo con esto, para un sonido de frecuencia de valor 987 Hz, progra- maremos e1temporizador con un valor: c = 1193180/987. 1* Emitir un sonido por el altavoz. Utilizar los puertos * 66, 67 y 97 para control del timer y del altavoz. *1 # include <stdio.h> # include <conio.h > main( ) [ int a, c, lb, hb; / long duracion = 100000; const int frec = 987; c = 11931801 frec; lb = c & OxFF; hb = c > > 8; outp(Ox43, OxB6); outp(Ox42,lb); outp(Ox42,hb); a = inp(Ox61); outp(Ox61,a I Ox3); while (--duracion > 0); outp(Ox61,a); ] 1* frecuencia sonido *1 1*frecuencia timer entre frecuencia sonido */ / * byte de menor peso *1 / * byte de mayor peso *1 / * indicar al timer env{o de datos */ / * enviar el byte de menor peso */ 1* enviar el byte de mayor peso */ / * salvar byte de control altavoz */ / * activar el altavoz */ / * duraci6n */ / * desactivar el altavoz */
  • 376. 3 Utilizando el Preprocesador • El Preprocesador de C
  • 377. INTRODUCCION / EI preprocesador de Microsoft C soporta todas las directrices definidas en ANSI C, las cuales permiten sustitucion de macros, compilacion condicio- nal e inclusion de ficheros fuente; y ademas aporta otras para el control de la numeracion de lineas en diagnosticos y en la depuracion, para gene- rar mensajes y para ejecutar acciones especificas del compilador. Las directrices para el preprocesador son utilizadas para hacer pro- gramas fuente faciles de cambiar y de compilar en diferentes situaciones. Una directriz comienza con el simbolo # como primer caracter, distinto de blanco, en una linea, e indica al preprocesador una accion especifica a ejecutar. Pueden aparecer en cualquier parte del fichero fuente, pero so- lamente se aplican desde su punta de definicion hasta el final del progra- ma fuente. La declaracion de una directriz puede ser continuada en una linea si- guiente, colocando el caracter inmediatamente antes del caracter nueva linea (NL). Cuando se ejecuta la orden para compilar un fichero fuente, el pre- procesador procesa el texto fuente, como primer paso, y antes de que el compilador analice dicho fichero. Este proceso consiste en:
  • 378. #define identificador texto # define identificador(parametros) texto 1. Si es necesario, se introducen caracteres NL para reemplazar 105 indicadores de fin de linea dependientes del sistema. 2. La secuencia NL es borrada y la linea siguiente es afiadida a la linea que contenia la s'ecuencia. 3. El texto fuente es descompuesto en tokens (elementos reconoci- dos por C) con el fin de localizar las llamadas a las macros. Cada comentario es reemplazado per un espacio en blanco. Una linea cuyo primer canicter es # es tratada como una orden por el pre- procesador. 4. Se ejecutan las directrices, se expanden las macros, las secuencias de escape son reemplazadas por sus equivalentes y las cadenas,de caracteres adyacentes son enlazadas. ~ DIRECTRIZ # define. Sustituci6n de srmbolos Esta directriz es utilizada para asociar identificadores con palabras clave, constantes, sentencias y expresiones. Cuando un identificador representa sentencias 0 expresiones se denomina macro. La directriz # define sustituye todas las apariciones identificador 0 iden· tificador(panimetros) en el fichero fuente por texto. Para mayor claridad del programa, las constantes simb61icas suelen expresarse en mayu.sculas con el fin de distinguirlas de las otras variables. Panimetros, representa una lista de parametros formales entre paren- tesis y separados por comas, que seran reemplazados por sus correspon- dientes parametres actuales en la sustituci6n. Entre el identificador yel parentesis abierto no puede haber un espacio en blanco, para no confundir los parametres con el texto.
  • 379. # define ANCHO 70 # define LONGITUD (ANCHO + 10) Este ejemplo define un identificador ANCHO como la con stante 70 y define LONGITUD como ANCHO + 10, esto es, 70 + 10. Cada ocu- rrencia de ANCHO en el fichero fuente es sustitufda por 70, y cada ocu- rrencia de LONGITUD por (70 + 10). Los parentesis son importantes, mas bien necesarios, para obtener los resultados esperados. Por ejemplo: En el caso de no haber utilizado parentesis en la definicion de LON- GITUD,el resultado serfa: Este ejemplo define una macro denominada MENOR. Por ejemplo, una ocurrencia en el programa fuente como:
  • 380. Este operador es utilizado solamente con macros que reciben argumentos. Este operador precediendo al nombre de un panimetro formal en la ma- cro, hace que el correspondiente panimetro actual pasado en la Hamada a la macro, sea inc1uido entre comillas para ser tratado como un literal. printf("Pulse una tecla para continuar" " n "); equivalente a: printf("Pulse una tecla para continuar n"); Este operador al igual que el anterior, tambien es utilizado con macros que reciben argumentos. Este operador permite la concatenaci6n de dos cadenas.
  • 381. printf("elementa " "1" " = %d n': elemental); printj("elementa 1 = %d n': elemental); Cuando se utiliza este formato, se sustituye esta linea por el contenido del fichero especificado. El fichero se busca en primer lugar en el directo- rio actual de trabajo y posteriormente en los directorios estandar definidos.
  • 382. Si se utiliza este otro formato, el fichero solamente es buscado en los directorios estandar definidos. Las siguientes directrices permiten compilar 0 no partes seleccionadas del fichero fuente. La sintaxis es la siguiente: # if expresion [grupo-de-ltneas;] [ # elif expresion 1 grupo-de-Hneas;] [ # elif expresion 2 grupo-de-Hneas;] [ # elif expresion N grupo-de-Hneas;] [# else grupo-de-Hneas;] #endif donde grupo-de-Hneas representa cualquier numero de lineas de texto de cualquier tipo. EI preprocesador selecciona un unico grupo-de-ltneas para pasarlo al compilador. EI grupo-de-ltneas seleccionado sera aquel que se correspon- da con un valor verdadero de la expresi6n que sigue a # if 0 # elif. Si todas las expresiones son falsas, entonces se ejecutara el grupo-de-ltneas a conti- nuaci6n de # else.
  • 383. # define EEUU ] #define ESPANA 2 # define FRANCIA 3 # if ESTADO---.ACTIVO = = EEUU char moneda[ ] = "dotar "; #elif ESTADO---.ACTIVO = = ESPANA char moneda[ ] = "peseta"; #elif ESTADO---.ACTIVO = = FRANCIA char moneda[ ] = "franco"; #endif main( ) ( Cada directriz # if en un fichero fuente debe emparejarse con su co- rrespondiente # endif. Una expresi6n puede contener el operador del preprocesador C, defi- ned, cuya sintaxis es: Este operador retorna un valor verdadero si el identificador esta ac- tualmente definido y retorna un valor falso, en caso contrario. # define REG] register # define REG2 register
  • 384. # if defined(M_86) # define REG3 # define REG4 # define REG5 # else # define REG3 register # if defined(M_68000) # define REG4 register # define REG5 register # else # define REG4 register # define REG5 #endif #endif func(a) REG3 jnt a; I REGl jnt b; REG2 jnt c; REG5 jnt d; En este ejemplo, las directrices # if y # endif controlan las declaracio- nes register en un fichero portable. Cuando se define ~86, el preproce- sador borra el identificador REG3 y REG5 del fichero reemplazandolo POI un texto nulo, 10 cual hace que a y d no puedan ser definidas como varia- bles de esa c1ase, mientras que silo son b y c. Cuando se define ~68000, las cuatro variables a, b, c y d son declaradas de c1ase register. Cuando no se definen ni M_86 ni M_68000, solamente a, by c son declaradas de c1ase register.
  • 385. # ifdef identificador # ifndef identificador # ifdej comprueba si el identificador esta definido e # ifndej comprueba si el identificador no esta definido. La linea # ifdej id es equivalente a # if dejined(id), y la linea # ifndej id es equivalente a # if !dejined(id). Estas directrices simplemente garantizan la compatibilidad con ver- sionesanteriores de C, ya que su funci6n es ejecutada perfectamente por el operador dejined(identificador). Estadirectriz va seguida de un numero entero y opcionalmente de un iden- tificador. Una linea de la forma indicada pone las constantes predefinidas ---LINE __ y ----.FILE __ a los valores indicados por cte-entera e identificador respectivamente, 10 cual hace que el compilador cambie su contador interne y su nombre de fichero de trabajo, por los valores especi- ficadosen estas constantes. Si se omite el nombre de fichero, se utiliza el que tenga la constante ----.FILE __ por defecto. La informaci6n proporcionada por hi directriz # line se utiliza sim- plementecon el objeto de dar mensajes de error mas informativos. Cuan- do se ejecuta esta directriz, la siguiente linea de texto a tomar es la indica- da por la constante entera especificada y del fichero especificado. EI compilador utiliza este numero de linea y fichero de informaci6n para dar los avisos y mensajes de error.
  • 386. Esta directriz es utilizada para abortar una compilaci6n, sacando el men- saje de error especificado a continuaci6n de la misma. Esta directriz tiene utili dad cuando en un programa incluimos un pro- ceso de compilaci6n condicional. Si se detecta una condici6n anormal, po- demos abortar la compilaci6n utilizando esta directriz, al mismo tiempo que se visualiza el mensaje de error especificado. # if !defined(MSDOS) # error MSDOS no dejinido: ver las opciones Iu 0 IV #endif Instruye al compilador para ejecutar una acci6n particular en tiempo de compilaci6n. Las opciones del compilador tienen efecto durante toda la compila· ci6n. Si deseamos que esto no suceda asi, podemos anular el efecto de la opci6n especificada, 0 por defecto, utilizando la directriz # pragma. A con- tinuaci6n exponemos algunas de ellas. Instruye al compilador para activar (on) 0 desactivar (off) el chequeo de punteros; esto es, comprobar si hay punteros nulos y punteros fuera de rango. Requiere la utilizaci6n de la opci6n IZr. Esta opci6n sera utilizada en la orden CL, junto con la opci6n Iqc (CL Iqc IZr progxx.c). La opdon por defecto depende de la opci6n correspondiente del compilador.
  • 387. Instruye al compilador para activar (on) 0 desactivar (off) el chequeo de la pila; esto es, comprobar si el tamafio de la pila es excedido. Por defecto, el chequeo de la pila esta activo (ver opcion ICe; opcion por defecto). La forma de proceder para desactivar el chequeo de la pila para una determinada funcion, asumiendo que dicho chequeo esta activo (opcion IOe), es la siguiente: #pragma check----.Stack( off) static void func(int argl, char *arg2) [ J #pragma check----.Stack( on) Instruye al compilador para que compile las funciones especificadas como funciones intrinsecas. Ver tambien la opcion IOi. Instruye al compilador para que compile las funciones especificadas como funciones estandar. Algunas funciones de la libreria estandar de C, est an implementadas de dos formas: como funciones estandar y como funciones intrinsecas. La fun- cion en su forma estandar, cuando se llama, utiliza el stack. La funcion en su forma intrinseca no necesita de una llamada para ser ejecutada; se genera como una funcion en linea (su codigo es expandido en la llamada). Los programas que utilizan funciones intrinsecas son mas rapidos; aunque pueden ser mas largos debido al codigo adicional generado. Las funciones
  • 388. de Microsoft C que podemos tratar como funciones intrinsecas son las si- guientes: abs( ), acos( ), acosl( ), asin( ), asinl( ), atan( ), atanl( ), atan2( ), atan21( ), cei/( ), ceil/( ), cos( ), cosl( ), cosh( ), coshl( ), exp( ), expl( ), jabs( ), jloor( ), jloorl( ), jmod( ), jmodl( ), labs( ), log( ), logl( ), loglO( ), loglOI( ), pow( ), powl( ), sin( ), sinl( ), sinh( ), sinhl( ), sqrt( ), sqrtl( ), tan( ), tanl( ), tanh( ), tanhl( ) Si utilizamos la opci6n del compilador /Oi, todas la funciones que aparezcan en el programa pertenecientes a este conjunto, senin tratadas como intrinsecas. Cuando deseemos tratar como intrinsecas s610 determinadas funciones, utilizaremos la directriz #pragma intrinsic(funcl[, junc2} ...}.
  • 389. La directriz # include afiade el contenido de un fichero dado, .h, en otro fichero. Los ficheros a incluir pueden ser utilizados para incorporar defi- niciones de constantes, macros, declaraciones de variables extemas y tipos complejos de datos, a cualquier fichero fuente. / ************************ PROGl102.C ************************ MODULO PRINCIPAL / * La siguiente linea incluye el jichero especijicado */ # include "inclllOJ.h" # include "stdio.h" main( ) / * FUNCION PRINCIPAL */ [ LeerRegistro( ); Verificar(); putchar( n'); puts(mensaje); ] / ************************ PROGl103.C ************************ MODULO PARA CONTENER LOS PROCEDIMIENTOS / * La siguiente linea incluye el jichero especijicado */ # include "incll102.h" # include "string.h" # include "stdlib.h" # include "stdio.h" void LeerRegistro( ) [ system("cls "); printf("Denominacion printj(' 'Existencias ] "); gets(registro.denominacion); "); scanf("%d': &registro.existencias);
  • 390. void Verificar() { if (registro.existencias < 5) strcpy(mensaje, "Por debajo de mfnimos"); else strcpy(mensaje, "Por encima de mfnimos"); #if !defined(~NCLllOl~) # define ~NCLllOl~ 1 void LeerRegistro(void); void Verificar(void); #if !defined(~NCLl102~) # define ~NCLl102~ 1 struct TipoReg { char denominacion[30]; int existencias; }; struct TipoReg registro; char mensaje[25];
  • 391. Este programa consta de cuatro m6dulos salvados en cuatro ficheros diferentes. Los ficheros .h seran incluidos por el preprocesador de C, cuando emitamos la orden para compilar. Este apartado presenta una utilidad sencilla que nos permitira medir el tiem- po de ejecuci6n de cualquier parte de nuestro programa C. En primer lugar crearemos un fichero "tiempo.h" que contendra las macros T_INICIAL(descripcion) y T~INAL. Cuando un programa fuen- te incluya estas macros, s6lo generara el c6digo correspondiente a ellas, si la orden de compilaci6n del programa define la macro TIEMPO eeL IDTIEMPO progxx.c) y se ha especificado el fichero de cabecera "tiem- po.h"; en otro caso, las referencias a estas macros seran nulas. La opci6n IDid[ = [valor]] define la constante simb6lica id para el pre- procesador. Si valor no se especifica, el valor de id es 1. / * cctiempo.h" Contiene las macros: T~NICIAL(DESCRIPCION) * T-FINAL */ # if !defined(TIEMPO~EFINIDO) #if defined(TIEMPO) # include <stdio.h> # include <time.h > clock_t inicial, final; #define T~NICIAL(DESCRIPCION) printf(CC n npara : %s': # descripcion); inicial = clock( ); #define T-FINAL final = clock( ); printf(CC ntiempo : %g seg': (do" ble)(fin ai-in icial) / (do" ble)CLOCKS~ ER_SEC); #define TIEMPO~EFINIDO #else # define T~NICIAL(DESCRIPCION) # define T-FINAL #endif #endif
  • 392. Observar el caracter de continuaci6n , en cada una de las lineas. No se pueden introducir comentarios dentro de la definici6n de una macro. Notar que el argumento descripci6n de la macro T~NICIAL no ne- cesita especificarse entre comillas, ya que utilizamos el operador #. T~NICIAL(lazo con variable unsigned int register); for (i = 0; i < 65535; i+ +); T---.FINAL; EI programa correspondiente a la descripci6n realizada, se expone a continuaci6n. int main(void) ( register unsigned int i; unsigned int k; T~NICIAL(lazo con variable unsigned int); for (k = 0; k < 65535; k+ +); T---.FINAL; para : lazo con variable unsigned int register tiempo : 0.06 seg para : lazo con variable unsigned int tiempo : 0.16 seg
  • 393. # include Htiempo.h" # include <math.h > int main(void) [ register int i = 0; float jvalor = 10.0; double dvalor = 10.0; long double ldvalor = 10.0; T~NICIAL(fmod( ) con argumentos jloat); for (i = 0; i < 32000; i+ +) fvalor + = jmod(fvalor, 2.0); LYINAL; T~NICIAL(fmod( ) con argumentos double); for (i = 0; i < 32000; i+ +) dvalor + = fmod(dvalor, 2.0); L ...FINAL; T~NICIAL(fmod( ) con argumentos long double); for (i = 0; i < 32000; i+ +) ldvalor + = jmod(ldvalor, 2.0); L...FINAL; )
  • 394. para : jmod( ) con argumentos jloat tiempo : 17.8 seg para : jmod( ) con argumentos double tiempo : 18.12 seg para : jmod( ) con argumentos long double tiempo : 18.68 seg para : jmod( ) con argumentos jloat tiempo : 24.39 seg para : jmod( ) con argumentos double tiempo : 21.42 seg para : jmod( ) con argumentos long double tiempo : 25.32 seg El preprocesador sustituye cada llamada a una macro por su definicion; dicho de otra forma, expande la macro. Las funciones no son expandidas; cuando se llama a una funci6n se utiliza la pila (stack) para almacenarla direcci6n de retorno y 10s argumentos pasados. # include Htiempo.h" # include <math.h > #pragma intrinsic(floor)
  • 395. main( ) ( double dvalorl i.nt i = 0; T~NICIAL(m6dulo (%) directamente); for (i = 0; i < 32000; i+ +) resto = dvalorl-jloor(dvalorl/dvalor2) *Clvalor2; T---.FINAL; T~NICIAL(m6dulo (%) empleando una macro); for (i = 0; i < 32000; i+ +) resto = MACRO~OD(dvalorl, dvalor2); T---.FINAL; T~NICIAL(m6dulo (%) empleando una junci6n); for (i = 0; i < 32000; i+ +) resto = junc_mod(dvalorl, dvalor2); T---.FINAL; I double junc_mod(double dvl, double dv2) ( return(dvl-jloor(dvl/dv2) *Clv2); I para : m6dulo (010) directamente tiempo : 25.32 seg para : m6dulo (%) empleando una macro tiempo : 25.38 seg para : m6dulo (%) empleando una funci6n tiempo : 25.81 seg
  • 396. 4 structuras Dinamicas y Algoritmos Estructuras Dinamicas y Algoritmos Algoritmos Recursivos de Ordenaci6n y de Busqueda
  • 397. La propiedad caracteristica de las estructuras dimimicas es la facultad que tienen para variar su tamafio y hay muchos problemas que requieren de estetipo de estructuras. Esta propiedad las distingue claramente de las es- tructuras estaticas fundamentales (arrays y estructuras). Por tanto, no es posible asignar una cantidad fija de memoria para una estructura dimimi- ca, y como consecuencia un compilador no puede asociar direcciones ex- plicitas con las componentes de tales estructuras. La tecnica que se utiliza mas frecuentemente para resolver este problema consiste en realizar una asignacion dinamica de memoria; es decir, asignaci6n de memoria para las componentes individuales, al tiempo que son creadas durante la ejecuci6n del programa, en vez de hacer la asignaci6n durante la compilaci6n del mismo. Cuando se trabaja con estructuras dinamicas, el compilador asigna una cantidad fija de memoria para mantener la direccion del componente asignado dimimicamente, en vez de hacer una asignaci6n para el compo- nente en si. Esto implica que debe haber una clara distinci6n entre datos y referencias a datos y que consecuentemente se deben emplear tipos de datos cuyos valores sean punteros 0 referencias a otros datos.
  • 398. Cuando se asigna memoria dimimicamente para un objeto de un tipo cual- quiera, se devue1veun puntero a la zona de memoria asignada. Para reali- zar esta operaci6n disponemos en C de la funci6n mal/oc(t). Esta funci6n asigna un bloque de memoria de t bytes y devuelveun puntero que referencia el espacio asignado. Si hay insuficiente espacio de memoria 0 si t es 0, la funci6n retorna un puntero nulo (NULL). Puesto que el puntero devuelto es a un objeto de un tipo no especifi- cado (void), utilizar la notaci6n cast sobre el valor devuelto, para realizar la conversi6n al tipo deseado. typedef struct datos elemento; struct datos [ / * miembros de la estructura */ elemento *NuevoElemento(void) [ return(( elemento *)mal/oc(sizeof(elemento ))); } main( ) [ elemento *c; c = NuevoElemento( ); / * asignaci6n dindmica de memoria */ if (Ic) [ printj(C(error: insuficiente espacio de memoria n");
  • 399. exit(l); } free(c); / * liberar el bloque de memoria apuntado por c */ } La funci6n NuevoElemento( ) asigna memoria para un objeto de tipo elemento y devuelve un puntero a dicho objeto. Si no hay espacio suficien- te para crear un objeto del tipo especificado, la funci6n NuevoElemento( ) devuelve un puntero nulo (0 0 NULL). En programas que utilizan asignaci6n dimimica de memoria, es muy importante liberar este espacio cuando no se utilice, ya que si se pierde la direcci6n que referencia el espacio asignado, dicho espacio no puede ser liberado y ademas permanece inaccesible. Para liberar un bloque de memoria asignado por la funci6n mal/ocr ), utilizaremos la funci6n freer ). Un array dinamico, segun se ha definido en el capitulo de punteros, una vez creado no permite alterar su tamafio. Si 10 que deseamos es una lista de elementos u objetos de cualquier tipo, originalmente vacia, que durante la ejecuci6n del programa vaya creciendo y decreciendo elemento a elemento, segunlas necesidades previstas en el programa, entonces tenemos que cons- truir una lista lineal en la que cada elemento apunte 0 direccione el siguiente. Por este motivo, una lista lineal se la denomina tambien Iista enlazada.
  • 400. Para construir una lista lineal primero tendremos que definir la clase de objetos que van a formar parte de la misma. De una forma generica el tipo definido sera de la forma: typedef struct id tipo_objeto; struct id / * declaraci6n de los miembros de la estructura d tipo_objeto ~iguiente; ]; typedef struct datos elemento; struct datos { int dato; elemento ~iguiente; elemento *p; p = NuevoElemento( ); p- >siguiente = NULL; Este ejemplo declara un tipo denominado elemento. Observamos que uno de sus miembros es un puntero a un objeto del mismo tipo. Esto per- mitini a un elemento creado referenciar su sucesor. La senten cia elemen- to *p declara un puntero p a un objeto de tipo elemento. La sentencia p=NuevoElemento( ) crea (asigna memoria para) un objeto de tipo ele- mento, genera un puntero (direcci6n de memoria) que referencia este nue- vo objeto y asigna esta direcci6n a la variable p. La sentencia p- >siguiente = NULL asigna al miembro siguiente del objeto apuntado por p el valor NULL, indicando as! que despues de este elemento no hay otro. Una declaraci6n como p = NULL indica una lista vacfa (suponiendo que p apunta al principio de la lista). Un objeto puede referenciarse por mas de un puntero; y tambien un objeto de un determinado tipo puede copiarse en otro objeto del mismo tipo.
  • 401. # include <stdio.h > # include <stdlib.h> typedef struct datos elemento; / * declaraci6n del tipo elemento */ struct datos [ int dato; elemento ~iguiente; elemento *NuevoElemento( ); void error(void); main( ) [ elemento *P, ..q, *r; / * Crear dos objetos de tipo elemento apuntados por p y q * respectivamente */ p = NuevoElemento( ); if (!p) error( ); q = NuevoElemento( ); if (!q) error( ); p->dato = 5; #J = *P / * copia el objeto apuntado por p en el objeto apuntado por q */ r = q; / * r apunta al mismo objeto que q */ printj("%d n': r- >dato * 2) / * escribe 10 */ J elemento *NuevoElemento( ) [ return ((elemento *)malloc(sizeof (elemento))); J
  • 402. 1.- Recorrido de una lista. 2.- Busqueda de un clemento en la lista. 3.- Inserci6n un elemento en la lista. 4.- Borrado de un clemento de la lista. void error(void) ( perror(Herror: insuficiente espacio de memoria n "); exit(l); } Las operaciones que podemos realizar con listas incluyen fundamentalmente las siguientes: typedef struct datos elemento; / * declaracion del tipo elemento */ struct datos { iot dato; elemento ~iguiente; }; Primero se crea un elemento y despues se reasignan los punteros, tal como se indica a continuaci6n: q = NuevoElemento( ); q->dato = n; q- >siguiente = p; p = q; / * asignacion de valores */ / * reasignacion de punteros */
  • 403. Este concepto nos sugiere como crear una Iista. Para ello, y partiendo de una lista vacfa, no tenemos mas que repetir la operacion de insertar un elemento al comienzo de una lista. Veamoslo a continuacion: elemento *P, ~; int n; printf((Introducir datos. Finalizar con AZ n n"); p = NULL; printf((dato: "); while (scanf((%d': &n) /= EOF) { q = NuevoElemento( ); q->dato = n; q->siguiente = p; p = q; printf((dato: "); J Notar que el orden de los elementos en la lista, es el inverso del orden en el que han llegado. La insercion de un elemento en la lista, a continuacion de otro elemento apuntado por p, es de la forma siguiente: q = NuevoElemento( ); q->dato = x; / * valor insertado */ q->siguiente = p- >siguiente; p- >siguiente = q;
  • 404. La inserci6n de un elemento en la lista antes de otro elemento apunta- do por p, se hace insertando un nuevo elemento detnis del elemento apun- tado por p, intercambiando previamente los valores del nuevo elemento y del elemento apuntado por p. q = NuevoElemento( ); *lJ = *P; p->dato = x; p- >siguiente = q; Para borrar el sucesor de un elemento apuntado por p, las operaciones a realizar son las siguientes: q = p- >siguiente; p- >siguiente = q- >siguiente; jree(q);
  • 405. Para borrar un elemento apuntado por p, las operaciones a realizar son las siguientes: q = Py siguiente; *P = *q; jree(q); Como ejercicio, escribir la secuencia de operaciones que nos permitan barrar el ultimo elemento de una lista. Recorrido de una Iista cuyo primer elemento esta apuntado par p Supongamos que hay que realizar una operaci6n con todos los elementos deuna lista, cuyo primer elemento esta apuntado por p. Por ejemplo, es- cribir el valor de cada elemento de la lista. La secuencia de operaciones es la siguiente:
  • 406. q = p; / * salvar el puntero al comienzo de la !ista */ while (q /= NULL) { printj("%d ': q- >dato); q = q- >siguiente; } La busqueda es secuencial y termina cuando se encuentra el elementa,0 bien, cuando se llega al final de la lista. . ~q = p; printf(t<valor: "); scanf(t<%d': &x); while (q /= NULL && q->dato /= x) q = q- >siguiente; Como ejercicio, realizamos a continuaci6n un programa que nos per- mita crear una lista clasificada, en la cual cada elemento conste de dos cam· pos: uno que contenga un numero entero y otro que sea un puntero a un elemento del mismo tipo. 1. Afiadir un elemento. Esta funci6n comprendeni dos casos: inser· tar un elemento al principio de la lista 0 insertar un elemento des· pues de otro. 2. Borrar un elemento de la lista. Esta funci6n buscani el elemento a borrar y despues 10 borrani. Hay que distinguir si se trata de la cabecera 0 de un elemento cualquiera.
  • 407. # include <stdio.h> # include <std!ib.h> # include <conio.h > # define ListaVacia (cabecera / * Lista simplemente enlazada. * Cada elemento contiene un n° entero. */ typedef struct datos elemento; struct datos / * tipo elemento */ / * elemento de una !ista de enteros */ [ fit dato; elemento ~iguiente; void error(void) [ perror(Herror: insuficiente espacio de memoria "); exit(l); l elemento *NuevoElemento( ) [ elemento *lJ = (elemento *)malloc(sizeof(elemento)); if (!q) error( ); return(q); l / * Funciones prototipo */ void menu(void); void anadir(elemento **, int); void borrar(elemento **, int); elemento *buscar(elemento *, int); void visua!izar(elemento *);
  • 408. main( ) { elemento ..cabecera = NULL; elemento .,q; iot opcion, dato, k=10; while (1) { do ( system(Hcls"); menu( ); opcion = getche( ); ~ while (opcion < '1' II opcion > '5'); system(' 'cis"); switch (opcion) ( case '1': printj(Hanadir dato: "); scanj(H%d': &dato); anadir(&cabecer~ datok break; case '2': printj(Hborrar dato: "); scanj(H%d': &dato); borrar(&cabecera, dato); break; case '3': printj(Hbuscar dato: "); scanj(H%d': &dato); q = buscar(cabecera, dato); if (q) q- >dato + = k; else printj(HLista vacfa n"); break; case '4': visualizar(cabecera); break; case '5': exit(O); } printj(H nPulse una tecla para continuar"); getch( ); } }
  • 409. void menu( ) ( printj(" n t1. printjt' n t2. printj(" n t3. printj(" n t4. printj(" n t5. printf(" n t ) Afiadir un elemento n"); Borrar un elemento n "); Buscar un elemento n"); Visualizar la lista n"); Salir n"); Elija la opcion deseada "); / * Introducir un elemento ordenadamente en la lista */ void anadir(elemento *~ab, int dato) ( elemento *cabecera = *cab; elemento ~ctual = cabecera, ~nterior = cabecera, *q; if (Lista Vacia) ( cabecera = NuevoElemento( ); cabecera->dato = dato; cabecera->siguiente = NULL; *cab = cabecera; return; ) / * Entrar en la lista y encontrar el punta de insercion */ while (actual /= NULL && dato > actual->dato) ( anterior = actual,' actual = actual- >siguiente; ) / * Dos casos: * 1) Insertar al principio de la lista * 2) Insertar despues de anterior (incluye insertar al final) */ q = NuevoElemento( ); if (anterior = = actual) ( q->dato = dato; q->siguien te = cabecera; cabecera = q; / * se genera un nuevo elemento */ / * insertar al principio */
  • 410. else ( q'- >dato = dato; q- >siguiente = actual,' anterior- >siguiente = q; 1 ..cab = cabecera; / * Encontrar un dato y borrarlo */ void borrar(elemento **cab, int dato) { elemento *cabecera = *cab; elemento *Uctual= cabecera, *Unterior=cabecera; if (Lista Vacia) ( printf(HLista vacfa n "); return; while (actual != NULL && dato != actual->dato) { anterior = actual; actual = actual- >siguiente; if (anterior = = actual) / * borrar el elemento de cabecera */ cabecera = cabecera- >siguiente; else / * borrar un elemento no cabecera */ anterior- > siguiente = actual- > siguiente;
  • 411. freeractual); *cab = ocabecera; / * Buscar un elemento determinado en la lista */ elemento *buscar(elemento ~abecera, int dato) [ elemento *actual = cabecera; while (actual != NULL && dato != actual->dato) actual = actual- >siguiente; return (actual); J / * Visualizar la lista */ void visualizar(elemento *cabecera) [ elemento *actual = cabecera; if (ListaVaria) printf("Lista vacfa n"); else [ while (actual != NULL) [ printf("%d ': actual- >dato); actual = actual- >siguiente; J printf(" ri"); Una pila es una lista lineal en la que todas las inserciones y supresiones (y normalmente todos los accesos), se hacen en un extremo de la lista. Un ejemplo de esta estructura es una pila de platos. En ella, el aiiadir 0 quitar platos se hace siempre por la parte superior de la pila. Este tipo de listas
  • 412. inserciones y supresiones reciben tambien el nombre de listas LIFO (last in first out - ultimo en en- trar, primero en salir). Las operaciones de insertar y suprimir en una pila, son conocidas en los lenguajes ensambladores como push y pop respectivamente. La operaci6n de recuperaci6n de un elemento de la pila, 16 elimina de la misma. Como ejemplo de utilizaci6n de una pila, vamos a simular una calcu- ladora capaz de realizar las operaciones de +, -, * y /. La mayoria de las calculadoras aceptan la notaci6n infija y unas pocas la notaci6n postfija. En estas ultimas, para sumar 10y 20 introduciriamos primero 10, despues 20 y por ultimo el +. Cuando se introducen los operandos, se colocan en una pila y cuando se introduce e1operador, se sacan dos operandos dela pila. La ventaja de la notaci6n postfija es que expresiones complejas pue- den evaluarse facilmente sin mucho c6digo. La calculadora de nuestro ejemplo utiliza la notaci6n postfija, par 10 que hemos desarrollado dos funciones: push y pop. La funci6n push intro- duce un valor en la pila y la funci6n pop saca un valor de la pila. 2. Analiza op; si se trata de un operando 10 mete en la pila utilizan- do la funci6n push( ); y si se trata de un operador saca, utilizando la funci6n pop( ), los dos ultimos operandos de la pila, realizala operaci6n indicada por dicho operador y mete el resultado enla pila para encadenarlo con otra posible operaci6n.
  • 413. # include <stdio.h > # include <stdlib.h> # define PitaVacia (cima = = NULL) typedef struct datos elemento; / * tipo elemento */ struct datos / * estructura de un elemento de la pita */ ! float dato; elemento *Siguiente; void error(void) ! perror("error: insuficiente espacio de memoria "); exit(l); } elemento *NuevoElemento( ) ! elemento *q = (elemento *)malloc(sizeof(elemento)); if (!q) error( ); return (q); } void push(elemento **P, float x); float pop(elemento **p); / * afiadir un dato a la pita */ / * sacar un dato de la pita */ main( ) / *funci6n principal */ ! elemento *cima = NULL; float a, b; char op[81]; system("cls "); printf("Calculadora con las operaciones: + - * / n"); printj("Los datos serdn introducidos de la forma: n");
  • 414. printf(H>operando 1 n"); printftc>operando 2 n"); printf(H>operador n n"); printf(HPara salir pulse q n n"); do [ printf(H> "); gets(op); switch (*op) [ b = pop(&cima); a = pop(&cima); printf(H%g n': a + b); push( &cima, a+b ); break; case c_': b = pop(&cima); a = pop(&cima); printf(H%g n': a - b); push( &cima, a-b ); break; case C *': b = pop(&cima); a = pop(&cima); printf(H%g n': a * b); push( &cima, a*b ); break; case 'i': b = pop(&cima); a = pop(&cima); if (b = = 0) ( printf(H nDivisi6n por cero n "); break; J printf(H%g n': a / b); push (&cima, a/b); break; default : push(&cima, atof(op)); J J while (*op != cq'); J
  • 415. / *Afiadir un dato a fa pila */ void push(efemento **P, float x) I efemento *Q, ..cima; q = NuevoEfemento( ); q->dato = x; q- >siguiente = cima; cima = q; / * Recuperar ef dato de fa cima de fa pi/a */ float pop(efemento **p) I efemento ..cima; float x; if (Pila Vacia) I printft' nerror: pop de una pila vacfa n "); return 0; I else x = cima- >dato; *p = cima- >siguiente; jree(cima); return (x); J J Calculadora con las operaciones: + - * / Los datos senin introducidos de la forma:
  • 416. >operando 1 >operando 2 >operador > 4.2 > 5 > * 21 >10 > - 11 >q Una cola es una lista lineal en la que todas las inserciones se hacen por un extrema; todas las supresiones (y normalmente todos los accesos) se hacen por el otro extremo de la lista. Por ejemplo, una fila en un banco. Este tipo de listas reciben tambien el nombre de listas FIFO (first in first out - primero en entrar, primero en salir). Este orden, es la unica forma de in- sertar y recuperar un elemento de la cola. ( -1_H_H_H_I- Tener en cuenta que la operaci6n de recuperaci6n de un e1emento de la cola, 10 elimina de la misma. Considerese el problema de almacenar las citas diarias, con el fin de ejecutarlas en el dia y hora sefialados. Cuando una de las citas se efectua, se quita de la lista. El program a que se expone a continuaci6n recoge este tipo de sucesos u otros. En el, empleamos dos funciones: introducir() y
  • 417. realizar( ). La funci6n introducir( ) nos permite afiadir nuevos sucesos a la cola y la funci6n realizar( ) saca un suceso de la cola. 1. Presenta un menu con las opciones de: Introducir un suceso, Rea- lizar un suceso y Salir. A continuaci6n nos solicita que elijamos una opci6n, 10 que da lugar a que se Harne a la funci6n corres- pondiente. 2. La funci6n introducir( ) comprendeni dos casos: afiadir un ele- mento a una cola vacia 0 afiadir un elemento al final de la cola. 3. La funci6n realizar( ) devuelve como resultado el suceso recupe- rado del principio de la cola. Si la cola estuviese vacia, se indicani con un mensaje. # include <stdio.h> # include <stdlib.h > # include <string.h > # include <conio.h > typedef struct datos elemento; / * tipo elemento */ struct datos / * estructura de un elemento de la cola */ [ char suceso[81]; elemento *Siguiente; void error(void) [ perror("error: insuficiente espacio de memoria"); exit(l); J
  • 418. elemento *NuevoElemento( ) { elemento *q = (elemento *)malloc(sizeof(elemento)); if (!q) error( ); return (q); J void menu(void); void introducir(elemento **, elemento **, char [ J); char *realizar(elemento **, elemento **); main( ) / *funci6n principal */ { elemento *principio, 4inal; char opcion, suceso[81]; principio = final = NULL; while (1) do ( system ("cis "); menu( ); opcion = toupper(getche( )); J while (opcion!= T && opcion!= 'R' && opcion != 'S'); system ("cis "); switch (opcion) ( case '1': printj(" nlntroduzca suceso: ' ), gets(suceso); introducir( &principio, &final, suceso); break; case 'R': strcpy(suceso, realizar(&principio, &final)); if (*Suceso) printj(" nRealizando el suceso %s n'~ suceso); print/(U nPulse una tec!a para continuar n "); getch( ); break;
  • 419. case '5': exit(O); ) ) ) void menu{ ) / * menu de opciones */ ( printj(CC n t Introducir suceso n "); printj(CC n t Realizar suceso n"); printj(CC n t 5alir n"); printf(cc n t Elija la opcion deseada ( 1,R, 5): "); ) / * Afiadir un dato a la cola */ void introducir(elemento **P, elemento **f, char suceso[ }) ( elemento *pc, 4c, *q; pc = *P; fe = *f," / *principio de la cola */ / *final de la cola */ q = NuevoElemento( ); strcpy(q- >suceso, suceso); q->siguiente = NULL; if (fc = = NULL) pc = fc = q; else fc = fc- >siguiente q; / * Recuperar un dato de la cola */ char *realizar(elemento **P, elemento **f) { elemento *pc, 4c, *q; char ~uceso;
  • 420. pc = *p; / *principio de la cola */ fc = 4; / *final de la cola */ if ( pc != NULL) { q = pc; suceso = (char *)malloc(strlen( q- >suceso) +1); strcpy(suceso, q- >suceso); pc = pc- >siguiente; if (pc = = NULL) fc = NULL; / * habra un solo suceso */ free(q); *p = pc; 4 = fc; return (suceso); } printf(CC nNo hay sucesos. n "); return 0; } Una lista circular es una lista lineal, en la que el ultimo elemento enlaza con el primero. Entonces es posible acceder a cualquier elemento de la lis- ta desde cualquier punta dado. Las operaciones sobre una lista circular re- sultan mas sencillas, ya que se evitan casos especiales. Cuando recorremos una lista circular, diremos que hemos llegadoal final de la misma, cuando nos encontremos de nuevo en el punta de parti- da; suponiendo, desde luego, que el punta de partida se guarda de alguna manera en la.lista, por ejemplo con un puntero fijo al mismo. Otra posible soluci6n al problema anterior seria poner en cada !isla circular, un elemento especial identificable como lugar de parada. Esteele- mento especial recibe el nombre de elemento de cabecera de la Usta. Esto presenta la ventaja de que la lista circular no estara nunca vacia. Una lista circular con un puntero al ultimo elemento, es equivalente a una lista lineal recta con dos punteros, uno al principio y otro al final.
  • 421. CABECERA ~ Como ejemplo de utilizaci6n de listas circulares, realizaremos la suma de ecuaciones algebraicas 0 polin6micas de las variables x, y, z. Por ejemplo: Cada polinomio sera representado como una lista en la que cad a ele- mento representa un termino no nulo, como se indica a continuaci6n: Aqui COEFICIENTE es el coeficiente del termino xA yB zC. Suponemos que los coeficientes y exponentes est an dentro de los rangos permitidos. La notaci6n ABC se utilizara para representar eI campo ±ABC de cada elemento tratado como un numero entero. El signo de ABC sera siempre positivo, excepto para el elemento de cabecera. Para este elemento, ABC=-l y COEFICIENTE = O. Los elementos de la lista apareceran sobre la mis- ma en orden decreciente del campo ABC, siguiendo la direcci6n de los en- laces. Por ejemplo, el polinomio 2x3y + 4xy3 - y4 se representaria: CABECERA ~
  • 422. A continuaci6n se muestra el programa correspondiente para sumar dos polino.mios, almacenados en dos listas circulares, denominadas par polP y polQ respectivamente. El campo ABC se corresponde con un entero igual A* 100 + B* 10 + C. Esto limita los exponentes a un digito. Si deseamos utilizar dos digitos, necesitariamos un entero de 6 digitos. Farmando de esta manera el campo ABC, es muy sencillo para aplicaciones posteriores, descomponerlo en Ios exponentes individuales. 1. Leer polinomio. Esta funci6n lee los terminos correspondientes a un polinomio determinado. La lectura se hace en orden creciente de Ivs exponentes ABC. Como consecuencia se crea una Iista cir- cular, que inicialmente constaba solamente del elemento cabecera. 2. Inicializar. Esta funci6n situa un puntero (actual) sobre el primer termino de un polinomio. 3. Comparar. Esta funci6n compara cada termino del polinomio P con los terminos del polinomio Q con el fin de sumar sobre Q los terminos de igual exponente, y afiadir a Q en orden decreciente de ABC, los terminos de P que no esten en Q. Para estas opera- ciones se utilizan las funciones "sumar coeficientes" e "insertar nuevo termino" respectivamente. 4. Eliminar termino. Esta funci6n elimina un termino nulo del poli- nomio Q, resultado de sumar un termino de P con el correspon- diente termino de Q. La terminologia empleada en el programa se interpreta de la forma siguiente: polP: identifica al polinomio P. Es una estructura que contiene tres punte- ros: cabecera que apunta el elemento cabecera de la lista que contiene al polinomio P, actual que apunta el termino del polinomio sobre el que esta- mos trabajando y anterior que apunta al termino anterior al actual.
  • 423. poIP->actual->siguiente: hace referencia al campo siguiente del elemen- to apuntado por el puntero actual del polinomio P. / * Listas circulares. Suma de ecuaciones algebraicas. * Cada termino es funci6n de las variables x, y, z * con exponentes a, b y c respectivamente, en el rango 0 a n. */ # include <stdio.h > # includ.e.--<std!ib.h> typedef struct datos elemento; / * tipo elemento */ typedef elemento * pelemento; / * tipo puntero a un elemento */ struct datos / * estructura de un elemento de la !ista */ { float coeficiente; int abc; pelemento siguiente; ]; / * coeficiente del termino en xyz */ / * exponentes de x, y, Z d typedef struct !ista ListCir; struct !ista { pelemento cabecera; pelemento anterior; pelemento actual; J; / * cabecera de la !ista circular */ / * elemento anterior al actual */ / * elemento actualmente apuntado */ void error(void) { perror("error: insuficiente espacio de memoria"); exi/(l); J
  • 424. pelemento NuevoElemento( ) [ pelemento q = (pelemento )malloc(sizeof(elemento)); if (!q) error( ); return (q); l void leer---po!inomio(ListCir *); void inicializar(ListCir *); void comparar(ListCir *, ListCir *); void sumar _coeficientes(ListCir *, ListCir *); void insertar_nuevo_termino(ListCir *, ListCir *); void e~iminar_termino(ListCir *); void es'cribir---po !inom io(ListCir); main( ) { ListCir polp, polQ; / * LEER POLINOMIOS polP y polQ */ leer---po!inomio(&polP); leer---polinomio(&polQ); / * INICIALIZAR */ inicia!izar(&polP); inicia!izar(&polQ); / * COMPARAR */ comparar(&polp, &polQ); /* ESCRIBIR POLINOMIO RESULTANTE Q */ escribir---po!inomio(polQ); l void leer---polinomio(ListCir *polX) / * Los nodos de la !ista se colocardn en orden decreciente * del campo abc, por 10 que hay que introducirlos en
  • 425. * orden inverso, esto es en orden creciente. */ [ iot abc,' float coef,' pelemento q,' / * Elemento de cabecera */ polX- >cabecera = NuevoElemento( ),' polX- >cabecera- >coejiciente = 0,' polX- >cabecera- >abc = -001,' polX- >cabecera- >siguiente = polX- >cabecera,' polX- > anterior = polX- > actual = NULL,' ~ / * Elementos restantes */ printj(t<Introducir los term inos del polinomio en orden creciente de abc (xAa . yAb . ZAC) n n n),' printjt'Para jinalizar, teclear coejiciente = 0 n nn),' printjt 'coejiciente: "),' scanf(t<%f': &coej),' while (coej) [ printf(t<exponentes abc (sin espacios).· n),' scanf(t<%d': &abc),' q = NuevoElemento( ),' q- > coejiciente = coef,' q- >abc = abc,' q- >siguiente = polX- >cabecera- >siguiente,' polX- >cabecera- >siguiente = q,' printf(t<coejiciente: n),' scanf(t<%f': &coej),' J J / * Inicializar proceso */ void inicializar(ListCir *poIX) [ polX- >anterior = polX- >cabecera,' polX- >actual = polX- >cabecera- >siguiente,' J
  • 426. / * Comparar los terminos de los polino.mios polP y polQ */ void comparar(ListCir *polp, ListCir *poIQ) { while (!(poIP->actual->abc < 0)) { while (poIP- >actual- >abc < polQ- >actual- >abc) { polQ- >anterior = polQ- >actual; polQ- >actual = polQ- >actual- >siguiente; } if (poIP- >actual- >abc = = polQ- >actual- >abc) sumar _coejfntes(po,P, poIQ); else h polP- >actual- >abc > polQ- >actual- >abc */ { insertar_nuevo_termino(polp, poIQ); polP- >actual = polP- >actual- >siguiente; } } } / * Sumar terminos con exponentes iguales; uno de P y otro de Q */ void sumar _coejicientes(ListCir *polP, ListCir *poIQ) { if ( polP- >actual- >abc < 0) return; else { polQ- >actual- >coejiciente + = polP- >actual- >coejiciente; if ( polQ- >actual- >coejiciente = = 0) { eliminar_termino(poIQ); polP- >actual = polP- >actual- >siguiente; } else { polP- >actual = polP- >actual- >siguiente; polQ- >anterior = polQ- >actual,' polQ- >actual = polQ- >actual- >siguiente; } } }
  • 427. / * El polinomio P contiene un terminG que no existe en Q d void insBrtar_nuevo_termino(ListCir *polp, ListCir *poIQ) { / * Se inserta antes del actual */ pelemento q; q = NuevoElemento( ); q- >coejiciente = polP- >actual- >coejiciente; q- >abc = polP- >actual- >abc; q- > siguiente = polQ- > actual; polQ- >anterior = polQ- >anterior- >siguiente = q; return; ) / * retornar a comparar */ / * Eliminar el terminG de coejiciente nulo */ void eliminar_termino(ListCir *poIQ) { pelemento q; q = polQ- >actual; polQ- >actual = polQ- >actual- >siguiente; polQ- >anterior- >siguiente = polQ- >actual; jree(q); / * liberar la memoria ocupada por el terminG nulo */ return; / * retornar a sumar _coejicientes */ void escribir-polinomio(ListCir polQ) { printjt' n nSuma de los polinomios: n n"); polQ.cabecera = poIQ.cabecera- >siguiente; while (polQ.cabecera- >abc != -1) { printf("coej" % +g exps. de x y z 0/003d n': poIQ.cabecera- >coejiciente, poIQ.cabecera- >abc); polQ.cabecera = polQ.cabecera- >siguiente; J J
  • 428. Una lista doblemente enlazada, es una lista lineal en la que cada elemento tiene dos enlaces, uno al elemento siguiente y otro al elemento anterior. Esto permite leer la lista en cualquier direcci6n. Las operaciones sobre una lista doblemente enlazada, normalmente se realizan sin ninguna dificultad. Sin embargo, casi siempre es mucho mas facilla manipulaci6n de las mismas, cuando se afiade un elemento de ca· becera y existe un doble enlace entre el ultimo elemento y el primero. Esta estructura recibe el nombre de Iista circular doblemente enlazada. Como ejemplo, vamos a construir una lista doblemente enlazada or- denada ascendentemente; cada elemento introducido se colocara automa- ticamente en el lugar que Ie corresponde. El programa consta fundamen- talmente de tres funciones: insertar( ), borrar( ) y visualizar( ). La funci6n insertar( ) comprende los casos: insertar un elemento al principio, insertar un elemento entre otros dos e insertar un elemento al final. La funci6n borrar( ) comprende: borrar el primer elemento y borrar un elemento cualquiera que no sea el primero. # include <stdio.h> # include <stdlib.h > # include < string.h > # include <eonio.h > # define Lista Vacia (listaD- >prine
  • 429. typedef struct datos elemento; / * tipo elemento */ typedef elemento * pelemento; / * tipo puntero a un elemento */ struct datos / * estruetura de un elemento de la lista */ ( pelemento siguiente; char clave[12]; pelemento anterior; J; typedef struct !ista ListDob; struct !ista ( pelemento prine; ) pelemento finat: J; / * prineipio de la !ista d / *final de la !ista */ void error(void) ( error("error: insuficiente espacio de memoria "); exit(l); J pelemento NuevoElemento( ) ( pelemento q = (pelemento )malloe(sizeof(elemento)); if (!q) error( ); return (q); J void insertar(ListDob *, char [ J); void borrar(ListDob *, char [ J); void visua!izar_!ista(ListDob); void menu(void); main( ) ( ListDob !istaD; char opcion, clave[12];
  • 430. listaD.princ while (1) { do { system ( 'cls' '); menu( ); opcion = toupper(getche( )); ] while (opcion!= '1' && opcion != opcion!= 'V' && opcion != system("cls"); switch (opcion) { case '1': ~ printf(" nlntroduzca fa clave a aiiadir: "); gets(clave); insertar(&listaD, clave); break; case 'B': printf(" nlntroduzca fa clave a borrar: "); gets(clave); borrar(&listaD, clave); break; case 'V': visualizar_lista(listaD); printf(" nPufse una tecla para continuar "); getch( ); break; case '5': 'B' && '5'); ] ] ] / * Aiiadir un dato a fa lista */ void insertar(ListDob *fistaD, char clave! }) { pefemento q, pactuaf, panterior; / * Generar un efemento */ q = NuevoEfemento( );
  • 431. strepy(q- >clave, clave); q- >anterior = q- >siguiente if (Lista Vacia) ( listaD- >prine return; J / * busear la posicion donde hay que insertar el pelemento */ paetual = panterior = listaD- >prine; while (paetual != NULL && stremp( clave, paetual- >clave) > 0) ( panterior = paetual; paetual = paetual- >siguiente; J ~ if (panterior = = paetual) ( q->siguiente = listaD- >prine; listaD->prine = paetual- > anterior = q; J else ( q->anterior = panterior; q->siguiente = paetual,' panterior- >siguiente = q; if (paetual) paetual- > anterior = q; / * paetual sera NULL cuando se inserta al final */ / * insertar despues de panterior */ / * incluye insertar al final */ / * Eneontrar una determinada clave y borrar el elemento */ void borrar(ListDob *listal), char clave! J) [ pelemento panterior, paetual; if (ListaVacia) return; / * Entrar en la lista y eneontrar el elemento a borrar */ panterior = paetual = listaD- >prine;
  • 432. while (pactual != NULL && strcmp( clave, pactual- >clave) != 0) { panterior = pactual; pactual = pactual- >siguiente; J / * Si el dato no se encuentra retornar */ if (pactual = = NULL) { printjt'%s no estd en la !ista n': clave); printj(" nPulse una tecla para continuar H); getch( ); return; J / * Si el dato se encuentra, borrar el elemento */ if (panterior = = pactual) / * el elemento estd al principia */ { ------- !istaD- >prine = !istaD- >princ- > siguiente; if (!istaD- >prine) !istaD- >princ- > anterior = NULL; / * Si principio es igual a NULL habfa un solo elemento */ J else / * borrar un elemento que no estd al principia */ { / * Modijicar el enlace siguiente */ panterior- >siguiente = pactual- >siguiente; / * Modijicar el enlace anterior excepto para el ultimo */ if (pactual- >siguiente) panterior- > siguiente- > anterior = pactual- > anterior; J jree(pactual); J / * Visua!izar el contenido de la !ista */ void visua!izar_!ista(ListDob !istaD) { pelemento pactual = !istaD.princ; while (pactual != NULL) { printf("%s n': pactual- >clave); pactual = pactual- >siguiente; J J
  • 433. void menu( ) I printj(" n t printj(" n t printf(" n t printj(" n t printj(" n t J Introducir un nuevo elemento n"); Borrar un elemento n "); VisuaUzar la Usta n "); SaUr n "); EUja la opcion deseada ( I, B, v, S): "); ListDob es una estructura que identifica la lista doblemente enlazada que estamos creando. Contiene dos punteros que definen perfectamente la lista:prine que apunta al primer elemento, y final que apunta al ultimo elemento. Para realizar las operaciones de inserci6n y borrado utilizamos dospunteros auxiliares: paetual que apunta al elemento identificado, y pan- teriar que apunta ~lemento anterior al identificado. Un arbol es una estructura no lineal formada por un conjunto de nodos y un conjunto de raffias. En un arbol existe un nodo especial denominado raiz.Un nodo del' que sale alguna rama, recibe el nombre de nodo de bi- furcacion 0 nodo rama y un nodo que no tiene ramas recibe el nombre de Dodo terminal 0 nodo hoja. nivel 0 ralz nivel 1 nodo de bifurcacidn nivel 2 nodo terminal Arbol De un modo mas formal, diremos que un arbol es un conjunto finito de uno 0 mas nodos tales que:
  • 434. b) Los nodos restantes estan agrupados en D > 0 conjuntos disjuntos AI' ... ,An' cada uno de los cuales es a su vez un arbol, que recibe el nombre de subarbol de la raiz. La definicion dada es recursiva, es decir, hemos definido un arbol como un conjunto de arboles. Esta es la forma mas apropiada de definir un arbol. De la definicion se desprende, que cada node de un arbol es la raiz de algun subarbol contenido en la totalidad del mismo. El numero de ra- mas de un node recibe e1 nombre de grado del nodo. El DiveI de un nodo respecto al node raiz se define diciendo que la raiz tiene nivel 0 y cualquier otro node tiene un nivel igual a la distancia de ese node al node raiz. EI maximo de los niveles se denomina profuDdidad 0 altura del arbol. Es utillimitar los arboles en el sentido de que cad a node sea a 10 sumo de grade 2. De esta forma cabe distinguir entre subarbol izquierdo y su- barbol derecho de un nodo. Los arboles asi formados, se denominan arbo- les binarios. --.-/ Un arbol binario es un conjunto finito de nodos que consta de un Dodo raiz que tiene dos sub arboles binarios denominados subarbol izquierdo y subarbol derecho. Evidentemente, la definicion dada es una definicion re- cursiva, es decir, cada subarbol es un arbol binario. Las formulas algebraicas, debido a que los operadores que intervie- nen son operadores binarios, nos dan un ejemplo de estructura en arbol binario. La figura siguiente nos muestra un arbol que corresponde a la ex- presion aritmetica:
  • 435. Esta definicion de arbol binario, sugiere una forma natural de representar ar- boles binarios en un ordenador: debemos tener dos enlaces (izdo y dcho) en cada nodo, y una variable de enlace raiz que nos direcciona el arbol. Esto es: typedef struct d~ nodo; struct datos / * estructura de un nodo del arbol */ [ / * declaraci6n de miembros */ nodo *izdo; nodo *dcho; ];
  • 436. Si el arbol esta vado, raiz es igual a NULL; en caso contrario, raIl es un puntero que direcciona la raiz del arbol, e izdo y dcho son punteros que direccionan los subarboles izquierdo y derecho de la raiz, respecti· vamente. Hay varios algoritmos para el manejo de estructuras en arbol. Una idea que aparece repetidamente en estos algoritmos es la noci6n de reco· rrido de un arboI. Este es un metoda para examinar sistematicamente los nodos de un arbol, de forma que cada node sea visitado solamente una vez. Pueden utilizarse tres formas principales para recorrer un arbol bina· rio: preorden, inorden y postorden. Cuando se visitan los nodos en preor· den, primero se visita la raiz, despues el subarbol izquierdo y por ultimo el subarbol derecho. Cuando se visitan los nodos en iilorden, primero se visita el subarbol izquierdo, despues la raiz y por ultimo el subarbol dere· cho. Cuando se visitan los nodos en postorden, primero se visita el subar- bol izquierdo, despues el subarbol derecho y por ultimo la raiz. R ,.:.. / preorden: R, I, D ....,. inorden: I, R, D postorden: I, D, R I D Evidentemente, las definiciones dadas son definiciones recursivas, ya que, recorrer un arbol utilizando cualquiera de ellas, implica recorrer sus subarboles empleando la misma definici6n. Si se aplican estas definiciones al arbol binario de la figura "f6rmulas algebraicas" anterior, se obtiene la siguiente soluci6n: Preorden: [norden: Postorden: * + albc-d*e! a+blc*d-e*! abcl+de!*-* El recorrido en preorden produce la notaci6n prefija; el recorrido en inorden produce la notaci6n convencional; y el recorrido en postorden pro· duce la notaci6n postfija 0 inversa.
  • 437. Los nombres de preorden, in orden y postorden derivan del lugar en el que se visita la raiz con respecto a sus subarboles. Estas tres formas, se exponen a continuaci6n como tres funciones recursivas. En ellas se utiliza la variable a significando la direcci6n de la raiz del arbol con el cual se opera. void preorden(nodo *a) { if (a /= NULL) ( / * operaciones con el nodo a */ preorden(a- >izdo); preorden(a- >dcho); ) ) void inorden(nodo ~) { if (a /= NULL) ( inorden(a- >izdo); / * operaciones con el nodo a */ inorden(a- >dcho); ) I void postorden(nodo ~) { if (a /= NULL) ( postorden( a- >izdo ); postorden( a- >dcho ); / * operaciones con el nodo a */ ) I Un arbol binario de busqueda es un arbol ordenado. Las ramas de cada nodo estan ordenadas de acuerdo con las siguientes reglas: para todo nodo
  • 438. aj' todas las claves del subarbol izquierdo de aj son menores que la clave de ai' y todas las daves del subarbol derecho de aj son mayores que la cia· ve de aj' Con un arbol de estas caracteristicas encontrar si un nodo de una cia· ve determinada existe 0 no, es una operaci6n muy sencilla. Por ejemplo, observando la figura siguiente, localizar la clave 12 es aplicar la definicion de arbol de busqueda, esto es, si la clave buscada es menor que la clave del nodo en el que estamos, pasamos al subarbol izquierdo de este nodo, para continuar la busqueda y si es mayor, pasamos al subarbol derecho. Este proceso continua hasta encontrar la clave 0 hasta llegar a un subarbol vacfo cuya raiz tiene un valor NULL. Como ejemplo, consideremos una secuencia de claves con el fin dede· terminar el numero de veces que aparece cada una de ellas. Esto significa que, empezando con un arbol vacfo, se busca cada clave en el arbol. Sise encuentra, se incrementa su contador y si no se encuentra, se inserta en el arbol como una nueva clave, con el contador correspondiente inicializa· do a 1. EI desarrollo completo se muestra a continuaci6n. EI proceso de bus- queda, funci6n buscar( ), se formula como una funci6n recursiva. Obser· var que al parametro formal raiz de la misma, se Ie pasa su correspondien· te parametro actual por referencia, con el fin de hacer posible los enlaces entre los nodos.
  • 439. Una vez construido el arbol, se utiliza la fund6n visualizar_arbol( ) para visualizar e1contenido del mismo. Los nodos se visitan en inorden y la soluci6n se presenta en forma de arbol, de acuerdo con el siguiente esquema: # include <stdio.h > # include <stdlib.h > typedef struct datos nodo; struct datos ( int clave; int contador; nodo *izdo; nodo ~cho; / * tipo nodo */ / * estructura de un nodo del arbol */ / *puntero a la ra{z del subarbol izquierdo */ / *puntero a la ra{z del subarbol derecho */ void error(void) ( perror("error: insuficiente espacio de memoria "); exit(l); I nodo */VuevolVodo( ) { nodo *q = (nodo *)malloc(sizeof(nodo)); if (!ql error( ); return (q); I
  • 440. void buscar(iot, nodo **); void visualizar_arbol(nodo *, iot); main ( ) [ iot k; system(Hcls"); printj (Hlntroducir claves. Finalizar con !l.Z n n"); printf(Hclave: "); while (scanf (H%d': &k) /= EOF) { buscar(k, &raiz); / * ra[z se pasa por referencia */ printj(Hclave: "); J system t<cls''); visualizar_arb 0l(raiz, 0); 1************************************************************** Buscar una clave en el arbol **************************************************************/ / * Buscar por un determinado nodo y si no esta insertarlo. * El valor para a es pasado por referencia para hacer posibles * los enlaces entre nodo y nodo cuando se crea uno de estos. */ #define ArbolVacio (a = = NULL) void buscar(iot x, nodo **raiz) [ a = *raiz; / * razz del arbol*/ if (ArboIVacio) / * el nodo con clave x, no esta en el arbol. */ { 1* Insertarlo */ a = NuevoNodo( ); a- >clave = x; a- >contador = 1;
  • 441. a->izdo a- >dcho NULL; J else if (x < a- >clave) / * el valor buscado estd a la izquierda de este nodo */ buscar(x, &a->izdo); else { if (x > a- >clave) / * el valor buscado estd a la derecha de este nodo */ buscar(x, &a->dcho); else / * el valor buscado existe */ a->contador+ +; /************************************************************** Visualizar el drbol **************************************************************/ / * Visualizar el drbol a con margen n. * Se emplea la forma inorden, para recorrer el drbol. */ void visualizar_arbol(nodo *a, iot n) { iot i; if (!ArboIVacio) ( visualizar_arbol(a- >izdo, n +1); for (i = 1; i < = n; i + +) printf(( "); printf((%d(%d) n': a- >clave, a- >contador); visualizar_arbol( a- >dcho, n +1); J J
  • 442. A continuacion se estudia el problema de borrar el nodo con clave x, de un arbol que tiene las claves ordenadas. Este proceso, es una tarea faciI si el nodo a borrar es un nodo terminal 0 si tiene un unico descendiente. La dificultad se presenta cuando deseamos borrar un nodo que tiene dos descendientes, ya que con un solo puntero no puede apuntarse en dos di· recciones. En este caso, el nodo a borrar debe ser reemplazado, bien por el nodo mas a la derecha en el subarbol izquierdo de dicho nodo, 0 bien por el nodo mas a la izquierda en el subarbol derecho. El proceso detallado, se presenta a continuacion y comprende lostres casos mencionados: 1. No hay un nodo con clave igual a x. 2. El nodo con clave x tiene un unico descendiente. 3. El nodo con clave x tiene dos descendientes. La funcion recursiva borrar_Dodo() se ejecuta solamente en el caso 3. En este caso, se desciende a 10 largo de la rama mas a la derecha del subarbol izquierdo del nodo apuntado por q que se va a borrar, y se reern- plaza la informacion de interes en el nodo apuntado por q por los valores correspondientes del nodo apuntado por d, que es el nodo mas a la dere· cha en el subarbol izquierdo. La funcion free(q) libera la memoria, delnodo que ya no forma parte del arbol.
  • 443. Observar que los valores para los panimetros formales raiz y dr, son pasados por referenda can el fin de realizar los enlaces necesarios. La Ha- mada a esta funci6n sera de la forma: typedef struct datos nodo; borrar(x, &ra{z); / * tipo nodo */ / * llamada a la /unci6n */ / * Funci6n para borrar un nodo cualquiera del arbol */ nodo *q; / * puntero al nodo a borrar */ void borrar(int x, nodo **raiz) [ nodo *p = *raiz; / * Descender por el arbol de ra{z p, para buscar el nodo * que se desea borrar */ if (p = = NULL) / * ;.arbol vacfo? */ printf("Esa componente no esta en el arbol n"); else if (x < p- >clave) borrar(x, &p- >izdo); else if (x > p- >clave) borrar( x, &p- >dcho ); else [ q = p; if (q->dcho = = NULL) p = q->izdo; else if (q- >izdo = = NULL) p = q->dcho; else borrar_nodo(&q- >izdo); J jree(q); *raiz = p; void borrar~odo(nodo **dr) [ nodo 4 = 4r; / * nodo con dos descendientes */ / * subarbol izquierdo */
  • 444. / * Descender al nodo mas a la derecha del subarbol d */ if (d- >dcho != NULL) borrar_nodo( &d- >dcho); else [ q- >clave = d- >clave; q- >contador = d- >contador; q = d; d = d->izdo; l ~r = d; Un arbol binario esta perfectamente equilibrado si, para todo nodo, elilt!- mero de nodos en el subarbol izquierdo y el numero de nodos en el subar- bol derecho, difieren como mucho en una unidad. n=1 0 Como ejemplo, considerese el problema de construir un arbol perfee- tamente equilibrado, siendo los valores de los nodos, n numeros que seleen de un fichero de datos, en nuestro caso del fichero predefinido stdin (fi- chero estandar de entrada). Esto puede realizarse facilmente distribuyendo los nodos, segun seleen, equitativamente a la izquierda y a la derecha de cada nodo. El proceso re- cursivo que se indica a continuaci6n, es la mejor forma de realizar esta
  • 445. distribuci6n. Para un numero dado n de nodos y siendo ni (nodos a la iz- quierda) y nd (nodos a la derecha) dos enteros, el proceso es el siguiente: 2. Generar el subarbol izquierdo con ni misma regIa. 3. Generar el subarbol derecho con nd = n-ni-l nodos utilizando la misma regIa. Cada nodo del arbol consta de los siguientes miembros: clave, punte- ro al subarbol izquierdo y puntero al subarbol derecho. El proceso detallado se presenta a continuaci6n y comprende una fun- cion recursiva denominada construir_arbol(), la cual construye un arbol de n nodos. # include <stdio.h> # include <stdlib.h> typedef struct datos nodo; struct datos [ int clave; nodo *izdo; nodo ~cho; }; / * tipo nodo */ / * estructura de un nodo del drbol */ / * puntero a la ra{z del subdrbol izquierdo */ / * puntero a la ra{z del subdrbol derecho */ void error(void) [ perror("error: insuficiente espacio de memoria"); exit(l); }
  • 446. nodo *lVuevolVodo( ) { nodo *q = (nodo *)malloc(sizeof(nodo}}; if (!q) error( }; return (q); J nodo *cohstruir_arbol(int}; void visualizar_arbol(nodo *, int}; main ( ) / * Funci6n Principal */ { system("c!s"}; printJ ('lVlimero de nodos: "); scanj("%d': &n}; printJ ("Introducir c!aves: n n"); raiz = construir _arbol(n}; / * construir arbol de n nodos */ system('c!s"}; visualizar_arbol(raiz, 0); /************************************************************** Funci6n construir arbol **************************************************************/ / * Construir un arbol de n nodos perJectamente equilibrado */ nodo *construir_arbol(int n} { nodo *q; int ni, nd; if (n = = 0) return (lVULL); else ni = n / 2; nd = n - ni - 1; q = lVuevolVodo( ); / * nodos del subarbol izquierdo */ / * nodos del subarbol derecho */
  • 447. printj(t<clave: "); scanf(t<%d': &q- >clave); q- > izdo = construir _arbol(ni); q- > dcho = construir _arbol(nd); return (q); } J /************************************************************** Visualizar el arbol **************************************************************/ / * Visualizar el arbol a con margen n. * Se emplea la forma inorden, para recorrer el arbol. */ void visualizar_arbol(nodo *0, int n) ! int i; if (a /= NULL) 1* si el arbol no esta vacfo ... */ ! visualizar_arbol(a- >izdo, n +1); for (i = 1; i < = n; i+ +) printj(t< "); printj(t<%d n': a- >clave); visualizar_arbol(a- >dcho, n +1); } }
  • 448. ALGORITMOS RECURSIVOS, DE ORDENACION Y DE BUSQUEDA En este capitulo vamos a exponer como resolver problemas muy comunes en la vida diaria. El primer problema que nos vamos a plantear es la recur- sion; estos son problemas cuyo planteamiento forma parte de su solucion. El segundo problema que vamos a abordar es la ordenacion de objetos en general; este es un problema tan comun que no necesita explicacion. Algo tan cotidiano como una guia telefonica, es un ejemplo de una lista clasifi- cada. Ellocalizar un determinado telefono exige una busqueda por algun metodo. El problema de busqueda sera el ultimo que resolveremos. Se dice que un proceso es recursivo si forma parte de si mismo 0 sea que se define en funcion de si mismo. La recursion aparece en la vida diaria, en problemas matematicos, en estructuras de datos y en muchos otros pro- blemas. La recursion es un proceso extremadamente potente, por 10 que la ana- lizaremos detenidamente para saber cuando y como aplicarla. Esto quiere
  • 449. decir que aunque un problema par definicion sea recursivo, no siempre este sera el metoda de solucion mas adecuado. En las aplicaciones practicas, antes de poner en marcha un proceso recursivo, es necesario demostrar que el nivel maximo de recursion, esto es el numero de veces que se va a llamar a sl mismo, es no solo finito, sino realmente pequeno. La razon es que se necesita cierta cantidad de memo- ria para almacenar el estado del proceso cada vez que se abandona, tem- poralmente, debido a una llamada para ejecutar un proceso que es el mis- mo. El estado en curso del proceso de calculo hay que almacenarlo, para recuperarlo cuando se acabe la nueva ejecucion del proceso y haya que rea- nudar la antigua. En terminos de lenguaje de programacion, una funcion es recursiva cuando se llama a sl misma. Un ejemplo es la funcion de Ackerman A, la cual esta definida para todos los valores enteros no negativos "m" y "n" de la forma siguiente: A(O,n) = n+l A(m,O) = A(m-l,l) A(m,n) = A(m-l,A(m,n-l» (m > 0) (m,n > 0) El pseudocodigo que nos muestra como solucionar este problema apli- cando la recursion, es el siguiente: A continuacion presentamos el programa correspondiente a este problema. <junci6n A (m,n) > IF (m es igual a 0) THEN devolver como resultado n +I ELSE IF (n es igual a 0) THEN devolver como resultado A(m-I,I) ELSE devolver como resultado A(m-I,A(m,n-I)) ENDIF END <junci6n A (m,n) >
  • 450. II I I I I I I I I I I I I I I I I I I I I I I I I I I main I I I I I I I I I I I I I I I I I I I I I I I I I I I I main( ) ! int m, n, a; printf(C<Cdlculo de A (m,n) =A (m-1,A (m,n-1)) n n"); printf(C<Valores de m y n : "); scanf(C<%d %d': &m, &n); a = Ackerman(m,n); printf(c<nnA(%d,%d) %dn': m, n, a); ] IIIIIIIIIIIIIIIIIIIIIIIIII Ackerman IIIIIIIIIIIIIIIIIIIIIIIIII int Ackerman(int m, int n) ! if (m = = 0) return n+1; else if (n = = 0) return Ackerman(m-1, 1); else return Ackerman(m-1, Ackerman(m,n-1)); Supongamos ahora que nos planteamos el problema de resolver la fun- cion de Ackerman, pero sin aplicar la recursi6n. Esto nos exigini salvar las variables necesarias del proceso en curso, cad a vez que la funci6n se Harne a sfmisma, con el fin de poder reanudarlo cuando finalice el nuevo proce- so invocado. La mejor forma de hacer esto es utilizar una pila, con el fin de alma- cenar los valores "m" y "n" cada vez que se invoque la funci6n para una nueva ejecuci6n y tomar estos valores de la cima de la pila, cuando esta nueva ejecuci6n finalice, con el fin de reanudar la antigua.
  • 451. <funcion A (m,n) > Declarar un array para utilizarlo como una pita, con el fin de almacenar los valores de: m,n Inicializar la pita con los valores m,n DO Tomar los datos de la parte superior de la pita IF (m es igual a 0) THEN meter en la pila el valor: n ELSE IF (n es igual a 0) THEN meter en la pita los valores: m-l,l ELSE meter en la pita el valor: m-l meter en la pita los valores: m,n-l ENDIF WHILE (p sea distinto de 0) devolver como resultado el valor n +1 END <fun cion A (m,n) > A continuaci6n presentamos el programa correspondiente a este problema. # include <stdio.h> # include <stdlib.h> typedef struct datos elemento; typedef elemento *pelemento; struct datos int m,n; pelemento siguiente; }; void error(void) { perror("error: no hay suficiente espacio en la pita n n "); exit(l); I
  • 452. pelemento NuevoElemento( ) [ pelemento q = (pelemento)malloc(sizeof(elemento)); if (!q) error( ); return (q); J int Ackerman(int, int); void mete-pi/a(pelemento *, int, int); void saca-pi/a(pelemento *, int *, int *); //////////////////////////// main //////////////////////////// main( ) [ int m, n, a; printjt'Cdlculo de A(m,n)=A(m-1,A(m,n-1)) n n"); printf("Valores de m y n : "); scanf("%d O/Od':&m, &n); a = Ackerman(m,n); printj(" n nA(%d,%d) %d n': m, n, a); J ////////////////////////// Ackerman ////////////////////////// int Ackerman(int m, int n) [ pelemento pi/a = NULL; / * pi/a de elementos (m,n) */ int Ackerman_m_n; mete-pila(&pi/a, m, n); while (1) [ / * tomar los datos de la cima de la pi/a */ saca-pi/a(&pi/a, &m, &n); if (m = = 0) h resultado para un elemento A(m,n) calculado d [ Ackerman_m_n = n +1; if (pi/a) [ saca-pila(&pi/a, &m, &n); mete-pi/a(&pi/a, m, Ackerman_m_n); J
  • 453. else return (Ackerman_m_n); l else if (n = = 0) mete-pi/a(&pi/a, m-1, 1); else [ mete-pi/a(&pi/a,m-1,Ackerman_m_n); / * n =Ackerman(m,n-l) */ mete-pi/a(&pi/a, m, n-1); l l l /////////////////////// meter en fa pi/a /////////////////////// void mete-pi/a(pefemento *P, int m, int n) [ pefemento q; q = NuevoEfemento( ); q->m = m, q->n = n; q- >siguiente = *P; *p=q; //////////////////////// sacar de fa pi/a //////1///////////////// void saca-pi/a(pefemento *P, int *pm, int *pn) [ pefemento q = *P; / * cabecera de fa pi/a ./ if (q = = NULL) [ printj(" nLista vacfa n "); exit(2); l else [ *pm = q->m, *pn = q->n; *P = q- >siguiente; jree(q); l l
  • 454. Un proceso en el cual es realmente eficaz aplicar la recursi6n es el pro- blema de las Torres de Hanoi. Este problema consiste en tres barras verti- cales A, Bye y "n" discos, de diferentes tamafios, apilados inicialmente sabre la barra A, en orden de tamafio decreciente. El abjetivo es mover los discas desde la barra A a la barra C, bajo las siguientes reglas: Una posible soluci6n, es el algoritmo recursivo que se muestra a con- tinuaci6n: nO discos origen centro destino inicialmente n A B C punta 1 n-l A C B punta 2 1 A B C punta 3 n-l B A C
  • 455. <funcion mover(n_discos, A, B, C) > IF (n_discos es mayor que 0) THEN mover(n_discos-l, A, C, B) mover disco de A a C mover(n_discos-l, B, A, C) ENDIF END <funcion A (m,n)> A continuaci6n presentamos el program a correspondiente a este problema. I I I I I I I I I I I II I I I I I I I I I I I I I I I main I I I I I / I I I I I I II I I II I I I IIIIIII main( ) { printj(HN° de discos: "); scanf(H%d': &n_discos); movimientos = mover(n_discos, ~: 'B: 'C'); printf(H nmovimientos efectuados: %d n': movimientos); I I I I I I I I I I I I I I I I I I I I I I I I I I I I mover I I I I I I I I I I I I I I I I I I I I I I I I I 11/ iot mover(iot n_discos, char a, char b, char c) { if (n_discos > 0) { mover(n_discos-l, a, c, b); printj(Hmover disco·de %c a %c n': a, c); movimientos+ +; mover(n_discos-l, b, a, c); J return movimientos;
  • 456. Como ejercicio se propone realizar este mi~mo problema, pero sin uti- lizar recursi6n. Uno de los procedimientos mas comunes y titiles en el procesamiento de datos, es la clasificaci6n u ordenaci6n de los mismos. Se considera orde- nar al proceso de reorganizar un conjunto dado de objetos en una secuen- cia determinada. El objetivo de este proceso general mente es facilitar la busqueda de uno 0 mas elementos pertenecientes a un conjunto. Como, ejemploconsiderense las listas de alumnos matriculados en una cierta asig- natura, las listas del censo, los indices alfabeticos de los libros, las guias telef6nicas, etc. Esto quiere decir que much os problemas estan relaciona- dosde alguna forma con el proceso de ordenaci6n. Es por 10 que la orde- naci6n es un problema importante a considerar. La ordenaci6n, tanto numerica como alfanumerica, sigue las mismas reglasque empleamos nosotros en la vida normal. Esto es, un dato nume- rico es mayor que otro, cuando su valor es mas grande, y una cadena de caractereses mayor que otra, cuando por orden alfabetico esta despues. Podemos agrupar los metodos de ordenaci6n en dos categorias: orde- naci6nde arrays u ordenaci6n interna, cuando los datos se guardan en me- moria interna, y ordenaci6n de ficheros u ordenaci6n externa, cuando los datos se guardan en memoria externa, generalmente discos. En este capitulo no tratamos de analizar exhaustivamente todos los metodosde ordenaci6n y ver sus prestaciones de eficiencia, rapidez, etc.; nosvamosa centrar en los metodos mas comunes para ordenaci6n de arrays y de ficheros. Haymuchas formas de clasificar datos y una de las mas conocidas es la clasificaci6npor el metodo de la burbuja. Veamosa continuaci6n el algoritmo correspondiente, para ordenar una listade menor a mayor, partiendo de que los datos a ordenar estan en una listade n elementos:
  • 457. 1.- Comparamos el primer elemento con el segundo, el segundo con el tercero, el tercero con el cuarto, etc. Cuando el resultado deuna comparaci6n sea "mayor que", se intercambian los valores de10s elementos comparados. Con esto conseguimos llevar el valor ma- yor a la posici6n o. 2.- Repetimos el punta 1, ahora para los 0-1 primeros elementos de la lista. Con esto conseguimos llevar el valor mayor de estos ala posici6n 0-1. 3.- Repetimos el punta 1, ahora para los 0-2 primeros elementos de la lista y as! sucesivamente. 4.- El proceso termina despues de repetir el punta 1,0-1 veces,0 cuando al finalizar la ejecuci6n del punta 1no haya habido ningun cambio. <junci6n clasijicar(array "a" de "n" elementos) > ["a" es un array cuyos elementos son aD' aI' ..., an_i n = n-l DO WHILE ("a" no este ordenado y n > 0) i = 1 DO WHILE (i < = n) IF ( a[i-l] > alii ) THEN permutar a[i-l] con a[i] ENDIF i = i+l ENDDO n = n-l ENDDO END <clasijicar > El siguiente ejemplo presenta la programaci6n de este algoritmo para el caso concreto de ordenar alfabeticamente una lista de cadenas de ca- racteres. # include <stdio.h> # include <stdlib.h>
  • 458. iot LeerLineas(char **, iot); void Clasijicar(char **, iot); void EscribirLineas(char **, iot); main( ) [ char **lineas; iot lineasmax; iot nlineas; / * puntero al array que contiene las lfneas */ / * mimero maximo de lfneas a ordenar */ / * mimero de lfneas lefdas */ printj(Hn 0 maximo de lfneas a ordenar: "); scanj(H%d': &lineasmax); / *Asignaci6n de memoria para lineas[lineasmax][MAXC] */ if (!(lineas = (char **)malloc(sizeof(char) *lineasmax*MAXC))) [ perror(Herror: insujiciente espacio de memoria n"); exit(l); 1 system(Hcls"); printf(HProceso de clasijicaci6n de lfneas de caracteres. n "); printj(HCada lfnea jinaliza con Enter. n n "); printjt'Entrada de lfneas. AZ para jinalizar. n"); if ((nlineas = LeerLineas(lineas, lineasmax)) > = 0) [ printj("Proceso de clasijicaci6n. n n"); Clasijicar(lineas, nlineas); EscribirLineas(lineas, nlineas); 1 else printjt'Demasiadas lfneas para clasijicar. n");
  • 459. iot LeerLineas(char **lineas, iot lineasmax) { iot nlineas,o char *p,o nlineas = -I; / * leer n lineas */ while ((p = gets(lineas[+ +nlineasJ)) /= NULL) { if (nlineas > lineasmax) return (-i); / * demasiadas lineas para ordenar */ } return (nlineas),o void Clasijicar(char **lineas, iot numero_de_lineas) { char aux[MAXCj,o iot i, s,o s = i,o while ((s i) && (--numero_de_lineas > 0)) { s = 0; / * no permutaci6n */ for (i = i,oi < = numero_de_lineas,o i+ +) / * i la linea (i-i) es mayor que la linea (i) ? */ if (strcmp(lineas[i-ij, lineas[i}) > 0) ( / *permutar las lineas (i-i) e (i) */ strcpy(aux, lineas[i-ij),o strcpy(lineas[i-ij, lineas[i}),o strcpy(lineas[i}, aux),o s = i,o / * permutaci6n */
  • 460. void EscribirLineas(cha'r **lineas, iot nlineas) ! while (nlineas-- > 0) printf("%s n': lineas[i+ +J); stoma el valor 1 cuando, al menos, se efectua un cambio entre dos ele- mentos. Si en una exploraci6n a 10 largo de la lista, no se efectua cambio alguno, s permaneceni valiendo 0, 10 que indica que la lista esta ordenada, terminando as! el proceso. Cuando se analiza un metoda de ordenaci6n, hay que determinar cuan- tas comparaciones e intercambios se realizan para el caso mas favorable, para el caso medio y para el caso mas desfavorable. En el metoda de la burbuja, en el caso mas desfavorable se realizan (0-1)(0/2)= (02-0)/2 comparaciones, donde 0 es el numero de elementos a ordenar. El numero de intercambios es 0 para el caso mas favorable (lista ordenada), 3(02-0)/4 para el caso medio y 3(02-0)/2 para el caso menos favorable(hay tres intercambios por cada elemento desordenado). El ana- lisismatematico que conduce a estos valores, queda fuera del prop6sito de este libro. El tiempo de ejecuci6n es un multiplo de 02 y esta directa- mente relacionado con el numero de comparaciones y de intercambios. El algoritmo para este metodo de ordenaci6n es el siguiente: inicialmente, seordenan los dos primeros elementos del array, luego se inserta el tercer elementoen la posici6n correcta con respecto a los dos primeros, a conti- nuaci6nse inserta el cuarto elemento en la posici6n correcta con respecto a lostres primeros elementos ya clasificados y as! sucesivamente hasta lle- gar al ultimo elemento del array.
  • 461. 478 ENCICLOPEDIA DEL LENGUAJE C Ejemplo: Valores iniciales: 46 54 12 30 84 18 10 77 T 46 54 12 30 84 18 10 77 1 T 12 46 54 30 84 18 10 77 , T 12 30 46 54 84 18 10 77 T 12 30 46 54 84 18 10 77 " T 12 18 30 46 54 84 10 77 ;: T 10 12 18 30 46 54 84 77 ,J Valores ordenados: 10 12 18 30 46 54 77 84 El pseudocodigo para este algoritmo puede ser el siguiente: <juncion insercion(array <fa"de <fn" elementos) > [<fa" es un array cuyos elementos son aO'aI' ..., an-i i = 1 DO WHILE ( i < = n) x = alii insertar x en la posicion correcta entre ao y ai ENDDO END <insercion > La programacion de este algoritmo, para el caso concreto de ordenar numericamente una lista de numeros, es la siguiente: # include <stdio.h > # include <std/ib.h> int /ista[ I = { 46, 54, 12, 30, 84, 18, 10, 77}; int n_elementos = sizeof(/ista)/sizeof(int);
  • 462. main() ! printf(HLista ordenada: n "); for (i = 0; i < n~elementos; i+ +) printf(H%6d': lista[i]); void insercion(int lista[ ], int n_elementos) ! / * desde el segundo elemento */ for (i = 1; i < n_elementos; i+ +) { x = lista[i]; k = i-I; while (k > =0 && x < lista[k]) { lista[k+1] = lista[k] k--; J lista[k+I] = x; /*para k=-I, se ha alcanzado*/ / * el extremo izquierdo. */ / * x es menor que lista[k] */ n-l (n2+n-2)/4 (n2+n)/2-1 caso medio caso menos favorable 2(n-l) (n2+9n-1O)/4 (n2 + 3n-4)/2
  • 463. Para el metoda de inserci6n, el tiempo de ej.ecuci6n es funcion de 02 y esta directamente relacionado con el numero de comparaciones y de intercambios. El metoda de ordenaci6n Quicksort, esta generalmente considerado como el mejor algoritmo de ordenaci6n disponible actual mente. 1.- Se selecciona un valor del array. Este valor se puede escoger aleatoria· mente 0 haciendo la media de un pequeno conjunto de valores torna- dos del array. El valor 6ptimo seria aquel que este precisamente enme- dio del rango de valores (mediana). No obstante, incluso en el peorde los casos (el valor escogido esta en un extremo), Quicksort funciona correctamente. 2.- Se divide el array en dos partes, una con todos los elementos menores que el valor seleccionado y otra con todos los elementos mayores0 iguales. 3.- Se repiten los puntos 1 y 2 para cada parte restante hasta que el array este ordenado. <!unci6n qs(array "a") > Se elige un valor x DO WHILE ( "a" no este ordenado ) [dividir "a" en dos partes: a_in! y .a~upJ a_in! con los elementos ai < x a~up con los elementos ai > = x ENDDO IF ( existe a_in!) THEN qs( a_in!) ENDIF
  • 464. IF ( existe a-----sup) THEN qs( a-----sup) ENDIF END <qs> A eontinuacion se muestra una version de este algoritmo, el eual se- leeeiona el elemento medio del array, 10 eual resulta faeil de implementar, aunque no siempre da lugar a una buena eleeeion. A pesar de ella, funcio- na eorreetamente. # include <stdio.h> # include <std!ib.h> int !ista!] = { 10, 3, 7, 5, 12, 1, 27, 3, 8, 13 }; int n_elementos = sizeof(!ista)/sizeof(int); main( ) ! printj(HLista ordenada: n"); for (i = 0; i < n_elementos; i+ +) printj(H%6d': !ista!i]); void quicksort(int !ista! ], int n_elementos) !
  • 465. '",.# ) ri' ,I'" / * Funci6n recursiva qs */" y u void qs(iot lista[ J, iot inf, iot sup) { register izq, der; iot mitad, x; U ~ (O} izq = inf,oder sup; mitad = lista[(izq+der)/2J; 0 do 1- ,1'1' "{ It I while (lista[izqJ < mitad && izq < sup) izq+ +; while (mitad < lista[derJ && der > inf) der--; if (izq < = der) 0 l ",t<"' {~ J 'f' x = lista[izqJ, lista[izqJ lista[derJ, lista[derJ = x; izq+ +; der--; } 1, } )b ~ i while (izq < = der); . if (i if <pder)qs(lista, inf, der); , .:>' if (izq < sup) qs(lista, izq, sup); } ~ f p~ ')';1 Amilisis del metoda quicksort: en el caso mas favorable en que, cada vez, se se1ecciona la mediana obteniendose dos particiones iguales, se rea- lizan o.log 0 comparaciones y 0/6.1og 0 intercambios, donde 0 es el mime- ro de elementos a ordenar; en el caso medio el rendimiento es inferior aI caso optimo en un factor de 2.log 2; y en el caso menos favorable en que, cada vez, se selecciona el valor mayor obteniendose una particion de 0-1 elementos y otra de 1elemento, el rendimiento es del orden de 0.0=02, Con el fin de mejorar el caso menos favorable, se sugiere elegir, cad a vez, un valor aleatoriamente 0 un valor que sea la median a de un pequeno con· junto de valores tornados del array. La funcion qs sin utilizar la recursion puede desarrollarse de la forma siguiente:
  • 466. void quicksort(i.ot lista[ J, iot n_elementos) [ qs(lista, 0, n_elementos - 1); 1 / * Funcion no recursiva qs */ void qs(i.ot lista[ J, iot inj, iot sup) [ # define NE 100 struct .elemento-pila ! iot inj, sup; 1 pila[NEJ; register izq, der; iot mitad, x, p; / * Inicializar la pila con los valores: inj, sup */ p = 1, pila[pJ.inf = inj, pila[pJ.sup = sup; do ! / * tomar los datos inj, sup de la parte superior de la pila */ inf = pila[pJ.inj, sup = pila[pJ.sup, p--; do ! / * Division del array en dos partes */ izq = inj,' der = sup; mitad = lista[(izq+der)/2J; do ! while (lista[izqJ < mitad && izq < sup) izq+ +; while (mitad < lista[derJ && der > inf) der--; if (izq < = der) ! x= lista[izqJ, lista[izqJ= lista[derJ, lista[derJ=x; izq + +,o der--,o 1 1 while (izq < = der),o
  • 467. if (izq < sup) { / * meter en la pi/a los valores: izq, sup */ P+ +, pi/a[p].inf = izq, pi/a[p}.sup = sup; J / * inf = inf; */ sup = der; J while (inf < der); J while (p); J En esta soluci6n obs~rvamos que despues de cada paso se generan do' nuevas sublistas. Una de ellas la tratamos en la siguiente iteraci6n y la otra la posponemos, guardando sus limites inf y sup en una pila. La tabla siguiente muestra los tiempos, en segundos, consumidos por 10 metodos de ordenaci6n estudiados anteriormente (sistema con micropro· cesador 80386SX y compilador Microsoft C 6). Los tiempos corresponden a la ordenaci6n de un array de 1000 elementos de tipo float. tiempo para ordenar un array que inicialmente ya esta or· denado. tiempo para ordenar un array que inicialmente esta ordena do en senti do inverso.
  • 468. tiempo para ordenar un array que inicialmente ya esta or- denado, pero, al que se Ie han permutado aleatoriamente dos de sus elementos. El metoda de la burbuja es el peor de los metodos; el metoda de in- serci6n directa mejora considerablemente y el metoda quicksort es el mas nipido y mejor metoda de ordenaci6n de arrays con diferencia. El objetivo de ordenar un conjunto de objetos, generalmente es facilitar la busqueda de uno 0 mas elementos pertenecientes a ese conjunto; aun- que es posible realizar dicha busqu~>dasin que el conjunto de objetos este ordenado, pero esto trae como consecuencia un mayor tiempo de proceso. Este metoda de busqueda, aunque valido, es el menos eficiente. Se basa en comparar el valor que se desea buscar con cada uno de los valores del array. El array no tiene por que estar clasificado. <funci6n Btisqueda--S( array a, valor que queremos buscar) > i = 0 DO WHILE ( no encontrado ) IF (valor = alii) encontrado ENDIF i = i+l ENDDO END <Btisqueda--S > Como ejercicio, escribir el c6digo correspondiente a un programa que permita buscar un valor en un array previamente leido.
  • 469. Un metodo eficiente de busqueda, que puede aplicarse a los arrays c1asifi· cados, es la busqueda binaria. Partiendo de que los elementos del arra} estan almacenados en orden ascendente, la busqueda binaria consisteen 10 siguiente: Se selecciona el elemento del centro 0 aproximadamente del centrodel array. Si el valor a buscar no coincide con el elemento seleccionado y e5 mayor que el, se continua la busqueda en la segunda mitad del array.Si, por el contrario, el valor a buscar es menor que el valor del elemento selec- cionado, la busqueda continua en la primera mitad del array. En ambos casos, se halla de nuevo el elemento central, correspondiente al nuevoin· tervalo de busqueda, repitiendose el cicIo. El proceso se repite hasta que se encuentra el valor a buscar, 0 bien hasta que el intervalo de busqueda sea nulo, 10 que querra decir que el elemento buscado no figura en el array. <funcion Blisqueda-B( array a, valor que queremos buscar ) > DO WHILE ( exista un intervalo donde buscar y no encontrado ) x = elemento mitad del intervalo de blisqueda IF (valor = x) THEN encontrado ELSE IF (valor > x) THEN buscar "x" en la segunda mitad del intervalo de blisqueda ENDIF IF (valor < x) THEN buscar "x" en la prim era mitad del intervalo de blisqueda ENDIF ENDIF ENDDO END <Blisqueda-B > # include <stdio.h > # include <stdlib.h >
  • 470. float lista[ I = { 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60 }; main( ) ( int posicion; float valor; printf(Hlntroducir el valor a buscar "); scanJtt%r: &valor); if (posicion / = -1) ptintj(H nLa posicion de %g es %d n': valor, posicion); / * La Juncion devuelve como resultado la posicion del valor. * Si el valor no se localiza se devuelve un resultado -1. */ int BusquedaB(f1oat lista[ I, float v, int inj, int sup) { int mitad; / * Comprobar si la blisqueda Jalla */ if ((inJ > = sup) && (lista[inf] /= v)) ( printj(HBlisqueda sin exito n"); return -1; J
  • 471. / * Sigue fa bLisqueda */ mitad = (inj + sup) / 2; if (lista[mitad] = = v) . ( printj(" nBLisqueda con exito n"); printf("Ef primer efemento ocupa fa posicion 0 n"); return mitad; } else if (v > lista[mitadJ) BusquedaB(lista, v, mitad + 1, sup); else BusquedaB(lista, v, in/, mitad - 1); Cuando el fichero es pequeno, esto es si tiene pocos registros, se puede co- piar en memoria en un array y utilizando las tecnicas vistas anteriormente, ordenar dicho array, copiando a continuaci6n el array ordenado en el fi- chero. Sin embargo, muchos ficheros son demasiado grandes para tratar· los de esta forma, por 10 que para ordenarlos recurriremos a tecnicas espe- ciales. EI siguiente programa desarrolla un algoritmo de ordenaci6n de un fiche- ro secuencial, denominado mezcla natural. La secuencia inicial de elemen- tos, viene dada en e1fichero c y se utilizan dos ficheros auxiliares denomi- nados a y b. Cada pasada consiste en una fase de distribucion que reparte equitativamente los tramos ordenados del fichero c sobre los ficheros ay b, y una fase que mezcla los tramos de los ficheros a y b sobre el ficheroc.
  • 472. b b '--~ '--2- fose de mezclo ----------.: fo':.e de dl':.~rtbuClon '--~ posada n Partimos de un fichero c. Con el fin de ilustrar el metoda de mezcla natu- ral, separemos los tram os ordenados en el mismo, por un gui6n ( - ). fichero a: 18 32 - 14 42 44 68 fichero b: 10 60 - 12 24 30 48 fichero a: 10 18 32 60 fichero b: 12 14 24 30 42 44 48 68 Observese que s610 se necesitan dos pasadas. El proceso finaliza, tan pronto como el numero de tramos ordenados del fichero c, sea 1. Una forma de reducir el numero de pasadas es distribuir los tramos ordenados sobre mas de dos ficheros.
  • 473. Este programa sugiere un nuevo ejercicio, que se deja para que ellec· tor 10 resuelva, cuyo enunciado es: dados dos ficheros ordenados a y b, obtener como resultado un fichero c tambien ordenado, que sea fusionde 10s dos ficheros anteriores. # include <stdio.h> # include <stdlib.h> const int FALSE = 0; const int TRUE = 1; void listar(FILE *pjx); void mezcla-'laturat(void); void distribucion(void); void copiar_tram 0(FILE *pjx, FILE *pjy); void copiar(FILE *pjx, FILE *pjy); void mezcla(void); void mezcla_tramo(void); typedef struct datos registro; struct datos { long clave; / * otros campos */ } reg; size_t t----.reg= sizeof(registro); / * tipo registro */ / * dejinicion de un registro */ / * registro */ / * tamano de un registro */ FILE *pjc; FILE *pja; FILE *pjb; / * puntero at jichero c */ / * puntero at jichero a */ / * puntero at jichero b */ int n_tramos; int jin_de_tramo; main( ) { char sclave[10};
  • 474. / * Abrir el jichero c para leer/escribir */ pjc = jopen("c': "w+b"); sjstem("cls' '); printj ("Pulse A Z para jinalizar n n"); printj ("Clave............ "); while (gets(sclave) /= NULL) ! reg.clave = atol(sclave); / * se leen el resto de los campos */ jwrite (&reg, t-,"eg, 1, pjc); printj ("Clave............ "); I listar(pjc); mezcla_natural( ); listar(pjc); jclose(pjc); l / * Visualizar todos los registros de un jichero */ void listar(FILE *pjx) ( system("cls "); rewind(pjx); / *posicionarse al principio del jichero */ / * Leer el primer registro del jichero */ jread (&reg, t_reg, 1, pjx); while (lfeof(pjx)) ! printf("%d ': reg.clave); / * escribir el resto de los campos */ / * Leer el siguiente registro del jichero */ jread (&reg, t_reg, 1, pjx);
  • 475. / * Algoritmo de ordenaci6n, mezcla natural */ void mezcla_natural(void) { do ( / * Crear y abrir los jicheros temporales a y b */ pja = tmpjile(); pfb = tmpjile(); rewind(pjc); distribucion( ); n_tramos = 0; rewind(pjc); rewind(pfb); rewind(pja); mezcla( ); rmtmp( ); / * borrar jicheros temporales */ J while (n_tramos != 1); J / * Repartir equitativamente los tramos ordenados de c en a y b */ void distribucion(void) / * desde c hacia a y b */ { do ( copiar_tramo(pjc, pja); if (!feof(pjc)) copiar_tramo(pjc, pfb); J while (!feof(pjc)); J / * copiar un tramo de x a y */ void copiar_tramo(FILE *pjx, FILE *pjy) { do ( J while (!fin_de_tramo); J void copiar(FILE *pjx, FILE *pjy) { long posicion; registro regx;
  • 476. jread (&reg, t-,eg, 1, pjx); if (feof(pjx)) jin_de~tramo = TRUE; else { jwrite (&reg, t-,eg, 1, pjy); / * Obtener el siguiente registro de x; verificar si se ha * lIegado al jinal de un tramo; recuperar la posicion. */ posicion = jtell(pjx); jread (&regx, t-,eg, 1, pjx); if (feof(pjx)) jin_de_tramo = TR UE; else ( jseek(pjx, posicion, SEEK--.SET); jin_de_tramo = reg.c1ave > regx.c1ave; J } J / * Mezclar tramos de jicheros a y b, ordenadamente sobre c */ void mezcla(void) / * desde a y b hacia c */ ( while (ljeof(pja) && Ijeof(pjb)) ( mezcla_tramo( ); n_tramos + = 1; J / * copiar el resto de los tramos del jichero no jinalizado */ while (ljeof(pja)) ( copiar_tramo(pja, pjc); n_tramos + = 1; J while (ljeoj(pjb)) ( copiar_tramo(pjb, pjc); n_tramos + = 1; J J
  • 477. / * intercalar un tramo de a y otro de b ordenadamente d void mezcla_tramo(void) { long posicion_a, posicion_b; registro rega, regb; do ( / * Obtener el siguiente registro de a y b; recuperar la * posicion; copiar ordenadamente en c. */ posicion_a = jtell(pja); posicion_b = jtell(pfb); jread (&rega, t-,eg, 1, pja); jread (&regb, t-,eg, 1, pfb); jseek(pja, posicion_a, SEEK_SET); jseek(pfb, posicion_b, SEEK~ET); if (rega.clave < = regb.clave) ( copiar(pja, pjc); if (fin_de_tramo) / * copiar el resto del tramo de b */ copiar_tramo(pfb, pjc); I else ( copiar(pfb, pjc); if (fin_de_tramo) copiar_tramo(pja, pjc); I } while (fjin_de_tramo); I Los ficheros de acceso aleatorio, a diferencia de 10s ficheros que solo pue- den ser accedidos secuencia1mente, permiten actua1izar 1a informacion sin tener que copiarla sobre otro fichero y pueden tratarse de forma amiloga a 10s arrays, 10 que simp1ifica enormemente 1a ordenacion de 10s mismos.
  • 478. Esto quiere decir que los metodos expuestos para ordenar arrays, pueden ser aplicados tambien para ordenar ficheros que pueden ser accedidos alea- toriamente. El siguiente programa ordena un fichero, en el cual cada registro esta formado por dos campos: referencia y precio. El desarrollo del programa variara en funci6n de la estructura de los datos y del tipo del campo (nu- merico 0 alfanumerico) que se utilice para la ordenaci6n del fichero. No- sotros vamos a ordenar el fichero por el campo referencia, de tipo alfabeti- co, empleando el metoda Quicksort explicado anteriormente. / * Metodo de ordenacion Quicksort para jicheros * accedidos aleatoriamente */ # include <stdio.h> # include <stdlib.h> # include <string.h> typedef struct datos registro; struct datos { char rejerencia[20}; long precio; I; registro reg; int t~eg = sizeof(registro); FILE *pj; / * tipo registro */ / * dejinicion de un registro */ / * registro */ / * tamano de un registro */ / *puntero al jichero */ void permutar ---fegistros(FILE *p/' int izq, int der); char *Campo(FILE *p/' int n); main( ) { register i; int n_elementos;
  • 479. if ((pj = jopen("datos': "r+b")) = = NULL) { printj("EI jichero "datos " no puede abrirse n"); exit(l); 1 system(Ccls"); jseek(pj, OL, SEEK--.END); n_elementos = jtell(pj)/t-feg; rewind(pf); quicksort(pj, n_elementos); printj("Fichero ordenado n"); jclose(pf); 1 void qs(FILE *pj, int inj, int sup); void quicksort(FILE *pj, int n_elementos) { qs(pj, 0, n_elementos - I); 1 void qs(FILE *pj, int inj, int sup) { register izq, der; char *mitad; izq = inj,· der = sup; / * Obtener el campo mitad por el que se va a ordenar, * del registro mitad */ strcpy(mitad, campo(pj, (int)(izq+der)/2)); do { while (strcmp(campo(pj,izq), mitad) < 0 && izq < sup) izq+ +; while (strcmp(mitad, campo(pj,der)) < 0 && der > inf) der--; if (izq < = der) { permutar -fegistros(pj, izq, der);
  • 480. izq + +; der--; l l while (izq < = der); if (inf < der) qs(pj, inj, der); if (izq < sup) qs(pj, izq, sup); l / * Permutar los registros de las posiciones izq y der */ void permutar -.registros(FILE *pj, iot izq, iot der) I registro x, y; jseek(pj, (loog)izq * t_reg, SEEK_SET); jread(&x, t_reg, 1, pf); jseek(pj, (loog)der * t_reg, SEEK~ET); jread(&y, t_reg, 1, pf); jseek(pj, (loog)izq * t-.reg, SEEK_SET); jwrite(&y, t-.reg, 1, pf); jseek(pj, (loog)der * t_reg, SEEK_SET); jwrite(&x, t-.reg, 1, pf); l / * Leer el campo utilizado para ordenar */ char *Campo(FILE *pj, iot n) I jseek(pj, (loog)n * t_reg, SEEK_SET); jread(&reg, t-feg, 1, pf); return (reg.referencia); l Losalgoritmos hash son metodos de busqueda, que proporcionan una "100- gitud de busqueda" pequefia y una flexibilidad superior a la de otros me- todos, como puede ser, el metodo de "busqueda binaria" que requiere que los elementos del array esten ordenados.
  • 481. Por "Iongitud de busqueda" se entiende el numero de accesos quees necesario efectuar sobre un array para encontrar el elemento deseado. Este metoda de busqueda permite, como operaciones basicas, ademas de la busqueda de un elemento, insertar un nuevo elemento (si el array esta vacio, crearlo) y eliminar un elemento existente. Un array producto de la aplicaci6n de un algoritmo hash se denomina "array hash" y son los arrays que se utilizan con mayor frecuencia en los sistemas de acceso. Graficamente estos arrays tienen la siguiente forma: El array se organiza con elementos formados por dos miembros: clave y contenido. La clave constituye el medio de acceso al array. Aplicando a la clave una funci6n de acceso "fa", previamente definida, obtenemos un numero entero positivo "i", que nos da la posici6n del elemento correspondiente, dentro del array. Conociendo la posici6n, tenemos acceso al contenido. El miembro con· tenido puede albergar directamente la informaci6n, 0 bien un punteroa dicha informaci6n, cuando esta sea muy extensa.
  • 482. El acceso, tal cual 10 hemos definido, recibe el nombre de "acceso directo". Como ejemplo, suponer que la clave de acceso se corresponde con el numero del documento nacional de identidad (dni) y que el contenido son los datos correspondientes a la persona que tiene ese dnL Una funci6n de acceso, i = fa(dni), que haga corresponder la posici6n del elemento en el array con el dni, es inmediata: la cual da lugar a un acceso directo. Esta funci6n as! definida presenta un inconveniente y es que el numero de val ores posibles de "i" es demasiado grande para utilizar un array de este tipo. Para solucionar este problema, es posible siempre, dado un array de longitud L, crear una "funcion de acceso", fa, de forma que nos genere un valor comprendido entre 0 y L, mas comunmente entre 1 y L. En este caso puede suceder que dos 0 mas claves den un mismo valor de "i": EI metoda hash esta basado en esta tecnica; el acceso al array es direc- to por el numero "i" y cuando se produce una colision (dos claves diferen- tes dan un mismo numero "i") este elemento se busca en una zona deno- minada "area de overflow". Este es uno de los metodos mas utilizados. El algoritmo para acceder a un elemento del array de longitud L, es el siguiente: 2. Si la posici6n "i" del array est a libre, se inserta la clave y el conte- nido. Si no est a libre y la clave es la misma, error: clave duplica-
  • 483. da. Si no esta libre y la clave es diferente, incrementamos "i" en una unidad y repetimos el proceso descrito en este punta 2. 5040 3721 4007 3900 6375 En la figura observamos que queremos insertar la clave 6383. Supon· gamos que aplicando la funci6n de acceso obtenemos un valor de 3, estoes, Como la posici6n 3 esta ocupada y la clave es diferente, tenemos que incre- mentar "i" y volver de nuevo al punta 2 del algoritmo. La "Iongitud media de btisqueda" en un "array hash abierto" viene dada por la expresi6n: Si existen 60 elementos en un array de longitud L = 100,el numero me- dio de accesos para localizar un elemento sera:
  • 484. En el metoda de "busqueda binaria", el numero de accesos viene dado por logz N, siendo N el numero de elementos del array. Para reducir al maximo el numero de colisiones y, como consecuen- cia, obtener una "Iongitud media de busqueda" baja, es importante elegir bien la funcion de acceso. Una "funcion de acceso" 0 "funcion hash" bastante utilizada y que proporciona una distribucion de las claves uniforme y aleatoria es la "fun- cion mitad del cuadrado" que dice: "dada una clave C, se eleva al cuadra- do (CZ) y se cogen n bits del medio, siendo 2" < = L. Supongamos: L 256 10 que implica n = 8 C 625 CZ 390625 ( 0 < = CZ < = 2Rl ) 39062510 00000000000001011111010111100001Z n bits del medio: 01011111z= 9510 Otra "funcion de acceso" muy utilizada es la "funcion modulo" (resto de una division entera): Cuando se utilice esta funcion es importante elegir un numero primo para L, con la finalidad de que el numero de colisiones sea pequeno. Una alternativa al metoda anterior es la de disponer de otro array separa- do, para insertar las claves que producen coli.sion, denominado "array de overflow", en el cual se almacenan todas estas claves de forma consecutiva.
  • 485. CLAVE CONTENIDO 5040 3722 2039 6383 Otra forma alternativa mas normal es organizar una lista encadenada por cada posicion del array donde se produzca una colision. 5040 3722 • -I 4007 • -I 2039 I [3-... 6383 I Cada elemento de esta estructura incorpora un nuevo miembro P, el cual es un puntero a la lista encadenada de overflow. En el metodo hash la eliminacion de un elemento no es tan simple como dejar vado dicho elemento, ya que esto da lugar a que los elementos inseT- tados por colision no puedan ser accedidos. Por ello se suele utilizar un miembro (campo) complementario que sirva para poner una marca de que dicho elemento esta eliminado. Esto permite acceder a otros elementos que depend en de el por colisiones, ya que la clave se conserva y tambien permi- te insertar un nuevo elemento en esa posicion cuando se de una nueva co- lision.
  • 486. Crear un array hash de una determinada 10ngitud L que permita almace- nar los datos numero de matricula y nombre de 10salumnos matriculados en una cierta Universidad, utilizando el metoda hash abierto y la funci6n de acceso modulo. <juncion hash(array, n_elementos, elemento x) > [El array estci iniciafizado a valor OJ i = matricula modulo n_elementos DO WHILE (no insertado y haya elementos fibres) IF (elemento Hi" estci fibre) THEN copiar elemento x en la posicion i ELSE IF (clave dupficada) THEN error: clave dupficada ELSE [se ha producido una cofisionJ [avanzar al siguiente elementoJ i = i+l IF (i = n_elemento) THEN i = 0 ENDIF ENDIF ENDIF ENDDO END <hash> # include <stdio.h> # include <stdfib.h> # include < math.h > # include <string.h>
  • 487. struct datos [ unsigned int matricula; char mombre; IIIIIIIIIIIIIIII rutina de manipulacion del error 111111111///11/ void error(void) [ perror((error: insuficiente espacio de memoria "); exit(l); void hash(elemento *, int, elemento); int siguiente-primo(int); main( ) [ elemento *a; int n_elementos; int i; char nom[81]; elemento x; 1* direccion de comienzo del array. 1* nOde elementos del array. printj((n 0 de elementos: "); scanft' %d': &n_elementos); n_elementos = siguiente-primo(n_elementos); I * crear el array dinamico «a" *1 a = (elemento *)calloc(n_elementos, sizeof(elemento)); if (Ia) error( ); 1* Introducir datos *1 printj((Introducir datos. Finalizar con matrfcula printj((motrirula: "); 1* Inicializar el array *1 for (i= 0; i < n_elementos; i+ +) [ a[i].matricula = 0; a[i].nombre = NULL;
  • 488. scanj("%u'~ &x.matricula); jjlush(stdin); while (x.matricula) ( printj("nombre: "); gets(nom); / * asignar espacio para nombre */ x.nombre = (char *)malloc(strlen(nom) +1); if (!x.nombre) error( ); strcpy (x.nombre, nom); hash(a, n_elementos, x); / * llamada a la junci6n hash */ printj("matrlcula: "); scanf("%u'~ &x.matricula); jjlush(stdin); void hash(elemento ~, int n_elementos, elemento x) { iot i; / * lndice para rejerenciar un elemento */ iot conta = 0, insertado = 0; / * contador */ i = x.matricula % n_elementos; / *junci6n de acceso */ while (linsertado && conta < n_elementos) [ if (a[i].matricula = = 0) ( a[i] = x; insertado ) else it (x.matricula = = a[i].matricula) ( printf("error: matrlcula duplicada n"); insertado = 1; ) else { / * Siguiente elemento libre */ i+ +, conta+ +; if (i = = n_elementos) i = 0;
  • 489. if (conta = = n_elementos) printft'error: array !lena n"); / / / / / / Buscar un ntimero primo a partir de un mimero dado / / / / // int siguiente-primo(int n) { if (n % 2 0) n++; while (!primo) { primo = 1; for (i = 3; i < = (int)sqrt((double)n); i + = 2) if (n % i = = 0) primo = 0; if (!primo) n + = 2; / * no primo */ / * siguiente impar */
  • 490. 5 Tecnicas Avanzadas • Manejo de la Memoria • Compilar y Enlazar • Librerias y Utilidades del Compilador • Rutinas en Lenguaje Ensamblador • Comunicaciones. Servicios del DOS y del BIOS • C y DOS • Control de Procesos
  • 491. DOS carga e1 c6digo y 10s datos correspondientes a un programa en seg- mentos en la memoria fisica (RAM). Cad a segmento es de un tamano de 64K. El numero minimo de segmentos asignados a un programa es dos, ya que e1c6digo y 10s datos se asignan en segmentos separados. De 10s mo- delos existentes, 10s mode10s de memoria pequenito (tiny) y pequeno (small) utilizan s01amente dos segmentos, otros mode10s de memoria, discutidos a continuaci6n, utilizan mas segmentos. El mode10 utilizado por defecto por e1 compilador Microsoft C es el modelo pequeno (small). Este puede cambiarse desde e1 entorno PWB 0 desde la orden de compi1ar emitida bajo e1 sistema operativo. Si e1 programa tiene mas de 64K de c6digo 0 mas de 64K de datos asignados estaticamente, uti1izaremos un mode10 de memoria diferente a1 modelo pequeno (small). 1. Compilar con 1a orden CL uti1izando 1a opci6n /A para especifi- car el mode10 de memoria adecuado: pequenito (tiny), pequeno
  • 492. (small), medio (medium), compacta (cQmpact), grande (large),0 enorme (huge). 2. Crear un modele mixto utilizando las palabras clave_near, -Jar, o _based. Una de las caracteristicas mas importantes dellenguaje C es que permite utilizar punteros para acceder directamente a la memoria. Cada programa C tiene al menos dos partes: el c6digo (definiciones de funciones) y los datos (variables y constantes). Cuando un programa se ejecuta, tanto el c6digo como los datos son referenciados por sus direc- ciones. Estas direcciones pueden almacenarse en variables declaradas como punteros. El tamafio de estas variables puede ser de 16 0 de 32 bits; esto depende del segmento de memoria donde se localicen los elementos refe- renciados. Los ordenadores que utilizan la familia de microprocesadores 80x86deIn- tel, gestionan la memoria en segmentos de 64K. Por 10tanto, para referen- ciar un elemento se necesita conocer, la direcci6n base del segmento y el desplazamiento dentro del segmento. Ellimite de los 64K es necesario porque los registros de la familia 80x86 son de 16bits; esto quiere decir, que un registro s610puede direccionar 65536 posiciones de memoria (64K). Segun 10expuesto, una variable de tipo puntero que especifique cual- quier posici6n de memoria, necesita 16 bits para la direcci6n base del seg- mento y otros 16 bits para el desplazamiento dentro del segmento. Enton- ces necesita un total 32 bits.
  • 493. En la familia 80x86 el registro CS contiene la direcci6n base del seg- mento de c6digo; el registro DS contiene la direcci6n base del segmento dedatos; el registro SS la direcci6n base del segmento de la pila; y el regis- tro ES la direcci6n base del segmento extra. El microprocesador 80386, tiene dos registros adicionales para direccionar otros segmentos: FS yES. Microsoft C utiliza por defecto el modele de memoria pequeno (small), quepermite utilizar 64K para el c6digo y otros 64K para los datos. CUaJ}- do se ejecuta un programa utilizando este modele, nunca cambian los re- gistrosCS y DS. Todos los punteros utilizados para referenciar tanto el c6- digoc6mo los datos son de 16bits, porque permanecemos dentro del limite de los 64K. Un puntero de 16bits que referencia un objeto dentro de un segmento de 64K se denomina puntero near. Sinuestro programa necesita mas de 64K para el c6digo 0 para los datos, necesitaremosutilizar punteros de32 bits, en lugar de 16bits. Estos punte- rospermiten apuntar a cualquier posici6n de la memoria; por ello, reciben el nombre de punteros far. Las operaciones con estos punteros, (asigna- cion,modificaci6n, etc.) requieren mas tiempo que las operaciones con los punteros near. La siguientefunci6n presenta en pantalla una ventana rellenada con un ca- nictercar al cual Ie asociamos un atributo de subrayado, alta intensidad, parpadeo, video inverso, normal 0 una combinaci6n de ellos. Bajo MS-DOS, la memoria intermedia de pantalla con adaptador mo- nocromoes de 4000 bytes, localizados a partir de la direcci6n 0 del seg- mentoOxBOOO.La memoria intermedia de pantalla con adaptador color gnificoses de 4000 bytes de longitud en modo texto y de 16384 bytes de
  • 494. longitud en modo grafico, localizados, en ambos casos, a partir de la di· reccion 0 del segmento OxB800. EI atributo de representacion en pantalla se localiza en la parte alta de la palabra; y el canicter a representar, en la parte baja. const int PFILA = 11; const int UFILA = 20; const int PCOLN = 21; const int UCOLN = 60; II primera fila de la ventana II ultima fila II prim era columna II ultima columna struct atributos { unsigned int primer -plano : 3; unsigned int intensidad : 1; unsigned int color-fondo : 3; unsigned int parpadeo : 1; }; atributos atributo; char car; II bits 0 a 2 II bit 3 II bits 4 a 6 II bits 7 1********************************************************"***** Rellenar una ventana en la pantalla con el cardcter car void escribir(char car, char atributo) { int _far *p; int fila, col; p = (int ~ar *)OxB8000000; II asignar a p B800:0000 for (fila = PFILA; fila < = UFILA; fila + +)
  • 495. for (col = PCOLN; col < = UCOLN; col + +) *(p + fila * COLS + col) = car I atributa < < 8; En esta funci6n, atributo contiene los atributos de los caracteres a re- presentar en la ventana. Estos atributos tienen que localizarse en la parte alta de la palabra de memoria correspondiente al canicter a representar. De ahi, el ejecutar la sentencia: la cual almacena en la palabra de direcci6np+ jila*COLS+col, el canicter en la parte baja y los atributos en la parte alta. La variable p contiene la direcci6n de comienzo de la pantalla, la direcci6n correspondiente a la fila 1 columna 1. Para acceder a una direcci6n fuera del segmento de datos en el cual estamos trabajando, utilizamos direcciones far. Como ejemplo observar la sentencia: Observar la cabecera de funci6n: void escribir(char car, char atributo) la cual indica, que la funci6n escribir debe recibir dos valores de tipo char: cary atributo. EI que el argumento atributa en esta funci6n sea de un tipo entero, es porque necesitamos realizar sobre eI un desplazamiento. Si he- mos definido atributa como una estructura de campos de bits, segun se observa en el ejemplo, la Hamada a la funci6n escribir tend ria que ser de la forma: escribir(car,(char)atributo); la cual daria lugar a un error, ya que atributo es una estructura. Para salvar este inconveniente recordar que un objeto de un determi- nado tipo puede ser convertido a otro tipo cualquiera, utilizando conver- siones explicitas de tipo sobre punteros como se indica a continuaci6n (ver Conversion explicita del tipo de una expresion en el capitulo 2). char ~trib = (char *)&atributo;
  • 496. Los punteros huge se utilizan para referenciar datos; no se pueden utilizar para referenciar codigo. Para un puntero far Microsoft C asume que un objeto, codigo a da- tos, esta dentro de un segmento de 64K; por ello las operaciones aritmeti· cas sobre punteros far se realizan sobre los 16bits que indican el desplaza- miento. Esta limitacion es superada utilizando los punteros huge; [as operaciones aritmeticas sobre este tipo de punteros se realizan con los32 bits correspondientes a la direccion de un elemento de datos. Esto permite que un unico elemento de datos supere el limite de los 64K. int juge *hp; int _far *hp; hp+ +; jp++; Utilizando punteros near, far y huge, son el compilador y el enlazador de C quienes se encargan del manejo de la memoria. Utilizando punteros basados en un segmento, somos nosotros quienes tenemos que especificar la direccion base del segmento. Las operaciones aritmeticas con punteros EI puntero hp es incrementado utilizando el valor de 32 bits quere- presenta la direccion segmentada (segmento mas desplazamiento). EIpun- tero jp es incrementado utilizando solo el valor de 16bits que indica eldes· plazamiento. Como una operacion aritmetica sobre 32 bits emplea mas tiempo que una sobre 16 bits el trabajo con punteros huge es mas leota que el trabajo con punteros far. Un puntero basado en un segmento (based) 9cupa 16bits y tiene la paten- cia y flexibilidad de un puntero far.
  • 497. basados en un segmento (based pointer) se realizan sobre los 16 bits que indican el desplazamiento. Si optamos por un tamano para todos los punteros no es necesario decla- rar cada variable como near 0 far. Lo mas simple es seleccionar un modelo de memoria estandar para que sea el compilador quien se encargue de este trabajo. El programa de instalaci6n SETUP de C, instala las librerias para cada uno de los modelos de memoria seleccionados. La utilizaci6n de estos mo- delos evitan que nosotros tengamos que programar utilizando las palabras clave _near y -Jar; es la forma mas simple de controlar el acceso al c6di- go y a los datos en un programa; yes la mejor forma de escribir un progra- ma portable. La desventaja es que con ellos, no siempre se obtiene el c6di- go mas eficiente. Memoria maxima Modelo Codigo Datos Arrays Pequenito (Tiny) <64K <64K <64K Pequeno (Small) 64K 64K 64K Medio (Medium) sin limite 64K 64K Compacto (Compact) 64K sin limite 64K Grande (Large) sin limite sin limite 64K Enorme (Huge) sin limite sin limite sin limite
  • 498. Cada modelo de memoria tiene su propia libreria, excepto el modele enorme (huge) que utiliza la Iibreria del modelo grande (large) y el modele pequenito (tiny) que utiliza la Iibreria del modelo pequeno (small). Lauti· Iizacion de una u otra Iibreria depende de la que elijamos nosotros enel momenta de compilar el programa. Cuando escribimos un program a C, hay que tener presentes dos limi· taciones que se aplican a los seis modelos de memoria: • Un unico modulo no puede generar mas de 64K de c6digo. Signifi· ca que un programa grande tiene que ser subdividido en modulos mas pequenos, que compilaremos separadamente y que enlazare· mos para formar un unico modulo ejecutable. • Un unico elemento de datos no puede exceder de 64K, a no serque aparezca en un programa compilado bajo e1mode1o de memoria huge, 0 que dicho elemento se haya declarado con la palabra clave Juge. EI modelo pequenito Iimita un programa a un tamano maximo de 64K, para codigo y datos combinados. La opcion IAT indica al compilador la utilizacion de este modelo. • Produce un fichero .COM que supera en ve10cidad de ejecuci6na su equivalente fichero .EXE. EI fichero .COM, es en realidad un programa generado con e1mode1o small y enlazado con la Iibreria crtcom.lib con e1fin de Iimitar a un unico modulo de 64K el c6digo y los datos. Por ejemplo: cI IAT Ie prog.c link Inoi Inoe Itiny crtcom.lib prog.obj,prog.com;
  • 499. Heap STACK -.BSS CONST ~ATA ; NULL _TEXT _psp En el modelo pequeiiito tanto e1c6digo como 10s datos, son accedi- dos con direcciones near. EImodelo pequeno utiIiza Ios dos segmentos por defecto, uno para c6digo y otTOpara datos. La opci6n /AS indica aI compilador Ia utiIizaci6n de estemodelo. Es la opci6n por defecto.
  • 500. Heap STACK _BSS CONST _DATA NULL _TEXT _psp t 64K Maximo t 256 bytes _ DS:OOOO,SS:OOOO _ CS:OOOO Por defecto, en el modelo pequeno, tanto al c6digo como a los datos se accede con direcciones near. El modelo medio utiliza varios segmentos para c6digo y un unico segmen· to para datos. La opci6n lAM indica al compilador la utilizaci6n de este modelo.
  • 501. Heap STACK _BSS CONST ~ATA NULL _TEXTJ 64K Maximo 64K Maximo m6dulo _TEXT --+------ t------.., - CS cargado _ DS:OOOO,SS:OOO _ CS:oooo Por defecto, en el modelo medio, al c6digo se accede con direcciones far y a los datos se accede con direcciones near. EI modelo compacta utiliza un segmento para c6digo y varios segmentos para datos. La opci6n lAC indica al compilador la utilizaci6n de este modelo.
  • 502. Far Heap Near Heap STACK _BSS CONST ~ATA NULL 64K Mciximo m6dulo~ATA _ DS cargado Por defecto,en el modelo compacto, at c6digo se accede con direccio- ·nes near y a los datos se accede con direcciones far. EI modelo grande utiliza varios segmentos para c6digo y varios segmentos para datos. La opci6n IAL indica at compilador la utilizaci6n de este modelo. Por defecto, en el modelo grande, tanto al c6digo como a los datos se accede con direcciones far.
  • 503. Far Heap Near Heap STACK --.BSS CONST -DATA NULL 64K Maximo m6dulo .-DATA DS d ---+.--- ~-----_l - carga 0 64K Maximo _TEXT......L ~-----_l _ CS:OOOO 64K Maximo mdoulo_ TEXT _ CS cargado 256 bytes _psp El significado de cada uno de los bloques de las figuras anteriores es el siguiente: El stack tiene un tamano de 2K por defecto y puede llegar a 64K. Este tamano es fijado en tiempo de com-
  • 504. pilacion. Utilizar la opcion "IF nUITL-hex" para asig- nar un valor, especificado en hexadecimal, diferente de 2K. El stack es utilizado para almacenar tempo- ralmente: datos locales (auto),los argumentos actua- les y la direccion de retorno cuando se llama a una funcion, y valores temporales generados al evaluar una expresion. Este segmento es inicializado a cero cada vez que se ejecuta un programa. Contiene datos estaticos y glo- bales no inicializados (excepto -far, _based, 0 _huge). Esto explica por que todas las variables ex- terny staticson automaticamente inicializadas a cera. Contiene datos const, cadenas de caracteres, y cons- tantes en coma flotante. Segmento de datos por defecto. Datos estaticos y glo- bales inicializados (excepto -far, _based, 0 _huge). Microsoft C define un segmento NULL, aparentemen- te de 8 bytes de longitud, (tamano del tipo mas gran- de; double)para chequear asignaciones a punteros nu- los, normalmente producidas por no haber asignado previamente memoria dinamica para el dato. Este seg- mento es inicializado a cero cada vez que se ejecuta un programa. Si hemos utilizado la opcion /Zr del compilador una asignacion en una posicion de esta memoria es inmediatamente detectada y el programa finaliza. Si no hemos utilizado la opcion IZr tales asig- naciones son detectadas y comentadas al finalizar el programa. Segmento de codigo por defecto para todos los mo- delos de memoria (excepto -far, _based, 0 _huge). segmento adicional para datos estaticos y globales ini- cializados y no inicializados -far, _based, 0 _huge. segmento adicional para codigo -far para los mo- delos medio, grande y enorme.
  • 505. (program segment prefix). ,Segmento de 256 bytes afia- dido al comienzo del programa. Contiene la direcci6n de la tabla de las variables de entorno para el progra- ma y una copia de la linea de 6rdenes de DOS nece- saria para los argumentos argc y argv de la funci6n main( ). El modelo enorme utiliza varios segmentos para c6digo y varios segmen- tos para datos. La opci6n IAH indica al compilador la utilizaci6n de este modelo. El modelo huge es parecido al modelo large, con la excepci6n de que soporta arrays mayores de 64K, con las siguientes limitaciones: • Un array auto no puede ser declarado _huge. Un array puede ser declarado _huge si es static, 0 si la memoria ocupada por el, es asignada por la funci6n halloc( ). • Para cualquier array mayor de 128K, todos los elementos deben te- ner un tamafio en bytes igual a una potencia de 2. Si el tamafio del array esta entre 64K y 128K, ambos inclusive, sus elementos pueden ser de cualquier tamafio. El tamafio en bytes de un array huge, es un valor de tipo unsigned long. Por ello, para calcular el tamafio de un array huge utilizando la funci6n sizeof( ) que devuelve un valor de tipo unsigned int hay que utilizar la no- taci6n cast. De forma similar debemos operar cuando realicemos una diferencia de punteros huge.
  • 506. /. Compi/ar bajo el modelo huge ./ # include <stdio.h> int a[40001J;/. tamano = 80002 bytes ./ main( ) ( a[OJ = 100; a[40000J = 200; printf(H%d %d n': a[OJ,a[40000J); I Por convenio un puntero nulo representa una direcci6n de memoria reser- vada para utilizarla como el valor centinela de una direcci6n de memoria invalida. Ellenguaje C define un puntero nulo por medio de la macro NULL: int i; char car = 's: *p = NULL; printj(Hdireccion de car: %lp n': (char -far *)&car); printj(Hdireccion de p: %lp n': (char -far *)&p); printj(Hp apunta a: %lp n n': (char -far *)p); / * Contenido del segmento NULL. * Direccion de comienzo = DS:OOOO */ EI programa que se expone a continuaci6n muestra como se detecta unaasignaci6n realizada sabre una direcci6n no valida. /. Compi/ar bajo el modelo small ./ # include <stdio.h > void main(void) {
  • 507. for (i = 0; i < 8; i+ +) printjr<%02X': *(p+i)); 1* Asignar un valor *1 *P = <n:· printj(H n nlo apuntado es: 1* Contenido del segmento NULL. * Direccion de comienzo = DS:OOOO *1 for (i = 0; i < 8; i + +) printj(H%02X ': *fP+i)); putchar« n'); I Si el programa se compila sin la opci6n IZr su ejecuci6n produce la salida: direcci6n de car: direcci6n de p: p apunta a: 22ED:ODCA 22ED:ODCC 22ED:OOOO run-time error R6001 - null pointer assignment Si el programa se compila con la opci6n IZr (cl /qc IZr prog.c) su eje- cuci6n produce la salida: direcci6n de car: direcci6n de p: p apunta a: 22ED:ODCA 22ED:ODCC 22ED:OOOO run-time error R6012 - ilegal near pointer use
  • 508. En el primer caso, Microsoft C chequea el segmento NULL antes de que el programa finalice para detectar si se ha escrito algo. Podemos ob· servar que se ha escrito el canicter "n" (6E). El programa finaliza y se yj. sualiza un mensaje de error sin ninguna indicaci6n de d6nde se ha produ- cido. La escritura sobre el segmento NULL se produce independientemente de que peste 0 no inicializado al valor NULL. En el segundo caso, en cuanto se produce la primera asignaci6n a una direcci6n no valida el programa termina y se visualiza el mensaje corres- pondiente. Esto quiere decir que el segmento NULL es chequeado cada vez que se hace una asignaci6n sobre una posici6n de memoria referencia- da por un determinado puntero. En un modele estandar todos los punteros referentes a datos son del mis- mo tamafio y todos los punteros referentes a c6digo tambien son del mis- mo tamafio. En cambio un modele de memoria mixto combina punteros de diferentes tamafios dentro del mismo programa. / * Compi/ar bajo el modelo small */ # include <stdio.h> int a[30000}, b[30000}; main( ) [ a[O} = 100; b[O} = 200; printft'%d %d n': a[O}, b[O]); 1 Al compilar este program a bajo el modelo small obtenemos un error debido a que los datos sobrepasan ellfmite de los 64K. Una primera solu- ci6n a este problema es elegir el modelo compact. Sin duda esta es la mejor elecci6n en cuanto a transportabilidad del programa. Una segunda solu- ci6n, y quizas mejor en cuanto a velocidad de ejecuci6n, es utilizar el mo-
  • 509. delo small que usa punteros near y declarar algunos elementos de datos como far. * Campi/ar bajo el modelo small */ # include <stdio.h > int aI30000], _far bI30000]; main( ) I alO] = 100; blO] = 200; printj(<t%d %d n': aIO],brO]); J Microsoft C permite utilizar punteros de diferentes tamafios, para 10 queafiade las palabras clave .-near, -far, _huge, y _based. Estas pala- bras no pertenecen allenguaje C estandar, sino que son una extensi6n de Microsoft C. Su significado cuando se utilizan con datos 0 con c6digo es el siguiente: Losdatos son direccionados con 16bits y se almacenan en el segmento de datos por defecto. Las funciones son direccionadas con 16 bits y se alma- cenan en el segmento de c6digo actual. Las operaciones aritmeticas con punteros se realizan con 16 bits. Losdatos son direccionados con 32 bits y pueden 10calizarse en cualquier parte de la memoria. Las funciones son direccionadas con 32 bits, por 10 quepueden ser llamadas desde cualquier posici6n de memoria. Las opera- ciones aritmeticas con punteros se realizan con 32 bits.
  • 510. Los datos son direccionados con 32 bits y pueden localizarse en cualquier parte de la memoria. Elementos individuales de datos (arrays) pueden 50- brepasar ellimite de los 64K. Estos punteros no se pueden aplicar sabre el codigo. Las operaciones aritmeticas con punteros se realizan con 32 bits. Los datos pueden localizarse en cualquier parte de la memoria y son direc- cionados con 16 bits mas una direccion base conocida, 10 que da lugar a un direccionamiento con 32 bits. Estos punteros no se pueden aplicar 50- bre el codigo. Las operaciones aritmeticas con punteros se realizan can 16 bits. ' Cuando declaramos punteros utilizando las palabras clave--'lear, -far, ~uge, 0 _based, en algunos casos puede ocurrir que sean incompatibles con las funciones de la libreria estandar. En estos casos, utilizando la no- tacion cast, un puntero near puede convertirse a un puntero far, porque el compilador afiade la direccion base del segmento correspondiente; pero si convertimos un puntero far a un puntero near, el compilador generani un mensaje para avisarnos de que el desplazamiento obtenido puede no corresponder al segmento de datos por defecto. Utilizando el modele small siempre podremos pasar a una funci6n el valor de un elemento declarado far pero no su direccion. / * Compi/ar en el modelo small */ # include <stdio.h > # include <time.h > long -for seg; main( ) [ time(&seg); / * direcci6n for i/egal */ printj("%ld n': seg); /* valor legal */ }
  • 511. La declaracion de un objeto 0 de un puntero a un objeto puede modificar- se utilizando las palabras clave: -'lear, -far, --'zuge, 0 _based. Cuando seutilizan estas palabras clave hay que tener en cuenta las siguientes reglas: • La palabra clave siempre modi fica al objeto 0 al puntero situado inmediatamente a su derecha. Por ejemplo: • Si a la derecha de la palabra clave declaramos un objeto, la palabra clave determina donde se ubicanl el objeto: en el segmento de datos por defecto (-'lear) 0 en un segmento de datos separado (-far, --'zuge, 0 _based). Por ejemplo: p es un puntero far a un objeto de tipo char, que sera almacenado en el segmento de datos por defecto independientemente del mode- 10 de memoria utilizado. • Si a la derecha de la palabra clave declaramos un puntero a un obje- to, la palabra clave determina el tipo de direccion que contendra el puntero: una direccion near (16 bits), una direccion far (32 bits),o una direccion huge (32 bits).
  • 512. En la declaraci6n de una funci6n s610 se pueden utilizar las palabras clave _near 0 -Jar. Cuando se utilizan hay que tener en cuenta las siguientes reglas: • La palabra clave siempre modifica la funci6n 0 el puntero a la fun- ci6n situado inmediatamente a su derecha. • Si a la derecha de la palabra clave declaramos una funci6n, la pala- bra clave determina c6mo se asignani la funci6n: near 0 far. Por ejemplo: jx( ) es una funci6n direccionada con 32 bits que retorna un valor de tipo char. • Si a la derecha de la palabra clave declaramos un puntero a una fun- ci6n, la palabra clave determina c6mo sera Hamada la funci6n: uti- lizando 16 bits (_near) 0 32 bits (-Jar). Por ejemplo: • La declaraci6n de una funci6n debe coincidir con la cabecera de la definici6n de dicha funci6n. Por ejemplo: char _far jx(void) [ EI concepto de punteros basados en un segmento (_based) fue introduci- do por Microsoft en el compilador C, versi6n 6.00. Este tipo de punteros
  • 513. esta restringido a tipos de datos, no pudiendose utilizar como punteros a funciones. Ocupan dos bytes y tienen la potencia y flexibilidad de un pun- tero far. Para declarar un puntero based Microsoft ha afiadido las siguientes palabras clave: Tipo puntero. Igual que _near, -Jar, 0 _huge. La palabra clave _based siempre va seguida de una expresion entre parente- sis que indica la direccion base. Tipo segmento. Permite declarar variables para contener direcciones de segmentos. Especifica una direccion base que es la co- rrespondiente a "segmento". Especifica una direccion base que es la co- rrespondiente al segmento al cual pertene- ce el puntero. _NULLSEG Segmento nulo. La definicion para esta con stante esta en malloc.h y es de la forma: ((_segment)O). _NULLOFF Desplazamiento nulo. La definicion para esta con stante esta en malloc.h y es de la forma:
  • 514. Operador base. Combina un segmento y un desplazamien- to, dando como resultado la direcci6n efectiva: ---Segment seg = OxlA47; iot _based(void) *l1esp (void *)Ox1234; inti a = *(seg:>desp); lA470 + 1234 / ***** Variables y punteros basados en un segmento constante ***** / * Compilado bajo el modelo small */ # include <stdio.h > int main(void) ( int _based(~egname(H_CODE")) *pl; int _based(~egname(H---.DATA")) *p2; iot _based(~egname(H_CONST")) *p3; iot _based(~egname(H_STACK")) *p4; iot _based(~egname(HMISEG")) *p5; int a=O, b[500]; b[lO]= 25; printj(H nCode printj(H nData %lp': %lp': (iot _far *)pl); (int _far *)p2);
  • 515. printf(H nConst printf(H nStack printf(H nMiseg I %lp': (int _far *)p3); %lp': (int _far *)p4); %lp n': (int _far *)p5); Code Data Const Stack Miseg 21D6:0000 22FO:OOR8 22FO:D88B 22FO:680E 22EF:A326 Observar que el resultado obtenido coincide con el mapa de memoria correspondiente al modelo small. Los segmentos DATA, CONST y STACK tienen su origen en el segmento de datos (DS). Observar tambien que se ha introducido un nuevo segmento, MlSEG, entre los segmentos de codigo y de datos; de forma similar a como se ubican 105 segmentos de datos en el modelo compact. La caracteristica clave de las variables de tipo ~egment es que pueden utilizarse para declarar otras variables _based. 1***********Punteros basados en un segmento variable ***********/ 1* Compi/ado bajo el modelo small */ # include <stdio.h> # include <string.h> # include <malloc.h > ~egment segmentol, segmento2; char _based(segmentol) *p, _based(segmento2) *q;
  • 516. void main(void) { if ((segmento1 = _bheapseg(2048)) = = .-NULLSEG) ( puts("error 1: segmento no asignado"); exit(l); J if ((segmento2 = _bheapseg(1024)) = = .-NULLSEG) ( puts("error 2: segmento no asignado"); exit(2); J if ((p = _bmalloc(segmento1, 81)) = = .-NULLOFF) ( puts("error 3: insujiciente espacio de memoria "); exit(3); J if ((q = _bmalloc(segmento2, 81)) = = .-NULLOFF) ( puts("error 4: insujiciente espacio de memoria"); exit(4); J printj("segmento 1 = %p n': segmento1); printj('p = %lp n': (char _far *)p); -fstrcpy((char _far *)p, (char _far *) "texto rejerenciado por el puntero p"); printj("%Fs n': (char _far *)p); printj(" nsegmento 2 = %p n': segmento2); printf("q = %lp n': (char _far *)q); -fstrcpy((char _far *)q, (char _far *) "texto rejerenciado por el puntero q "); printj("%Fs n': (char _far *)q); / * Liberar memoria */ _bjreeseg(segmento1); _bjree(segmento1, p); _bjreeseg(segmento2); _bjree(segmento2, q); J
  • 517. segmento 1 = 3330 p = 3330:0016 texto referenciado por el puntero p segmento 2 = 33B3 p = 33B3:0016 texto referenciado por el puntero q La funcion _bmalloc asigna, dentro del segmento especificado, un bloque de memoria igual al numero de bytes indicados. Si no hay suficien- te espacio de memoria, la funcion devuelve el valor ~ULLOFF. La fun- cion _bfree( ) libera el bloque de memoria asignado por la funcion _bmalloc. Observar que para copiar una cadena.de caracteres en otra hemos uti- lizado la funcion -!strcpy( ), la cual acepta como argumentos punteros far. Aunque la compilacion se ha hecho bajo el modelo small, no podemos utilizar la funcion strcpy( ) porque esta espera como argumentos punteros near. La funcion _bheapseg( ) da como resultado la direccion base de un seg- mento creado en el area de memoria libre. Si el segmento no se puede crear, esta funcion devuelve el valor -.NULLSEG (segmento nulo).
  • 518. Esta funci6n libera el segmento de memoria creado par la funcian _bheapseg( ). Esta funci6n retorna un valor 0 si se ejecuta satisfactoriamente 0 un valor -1 si ocurre un error. Un puntero based puede utilizar como direcci6n base otro puntero. Este tipo se aplica s610 a variables que sean punteros. El siguiente programa utiliza un puntero pb, basado en el puntero base, para almacenar valores float en el buffer mas adecuado entre varios de- finidos. / *************** Puntero basado en otro puntero ***************1 / * Compi/ado bajo el modelo small */ / * Declaracion de buffers */ char bufferJ[200]; char buffer2[400]; char buffer3[800]; char buffer4[J600]; char *base; float _based(base) *pb; / * Almacenar las direcciones de los buffers */ char ~irbuf[ ] = {bufferJ, buffer2, buffed, buffer4};
  • 519. 1*Almacenar los tamanos de los buffers */ iot tambuf[ ] = (sizeof(buffer1)/sizeof(f1oat), sizeof(buffer2)/sizeof(f1oat), sizeof(bufferJ) /sizeof(f1oat), sizeof(buffer4)/sizeof(f1oat) I; 1* numbuf = nOde buffers */ iot numbuf = sizeof(tambuf)/sizeof(int); void main(void) ( iot n_elem, i = 0; system(Hcls"); printjt';.Cudntos valores se van a almacenar? "); scanj(H%d': &n_elem); 1* Buscar un buffer de tamano adecuado */ while (i < numbuf) if (tambuf[iJ < n_elem) i++; else break; if (i = = numbuf) ( puts(Hbuffer no disponible"); exit(l); I base = dirbuf[iJ; printj(Hutilizando el buffer %d n': i); printj(Hdisponibles %d elementos n': tambuf[i]); printj(Halmacenando los %d valores: n n': n_elem); for (i = 0; i < n_elem; i+ +) { pb[i] = 3.1416 * i * i; printjt<%10.2f r': pb[i]); Este ejemplo crea cuatro buffers y elige el mas adecuado para almace- nar un conjunto de valores.
  • 520. La declaraci6n de un puntero _based(void) crea un puntero generico que actua igual que un desplazamiento dentro de un segmento. Esto quierede- cir que podremos definir cualquier segmento y combinarlo con un puntero declarado _based(void) utilizando el operador ": >". Este tipo se apliea s610 a variables de tipo puntero. El siguiente ejemplo utiliza la variable segmento de tipo ----.Segment para definir el comienzo del segmento; y el'puntero pb como desplazamiento dentro del segmento. / ******************* Puntero basado en void *******************/ / * Compilado bajo el modelo small */ -segment segmento = OxB800; / * Sustituir por OxBOOOpara mono */ int _based(void) *pb = 0; void main(void) { int fila, col; FILE *pf; / * Volcado de la pantalla al fichero "pan" */ if ((pf = fopen("pan':'w")) = = NULL) ( puts("error: no se puede abrir el fichero"); exit(l); J for (fila = 0;fila < 25; fila + +) { for (coI = 0; col < 80; col+ +, pb+ +) fputc((char)*(segmento: >pb), pf); fputc(' n: pf);
  • 521. Este ejemplo realiza un volcado de una Pe;mtalla de texto sobre un fi- chero denominado "pan". Cada posici6n de la pantalla en modo texto est a representada por dos bytes. El byte de menor peso contiene el caracter y el byte de mayor peso sus atributos. En el fichero s610 salvamos el caracter. Los punteros basados en su propio segmento (self) son titHes cuando de- claramos objetos based que contienen miembros que son punteros. Este tipo se aplica s610 a variables de tipo puntero. Por ejemplo: la definici6n de un nodo de una estructura en arbol. 1************Puntero basado en su propio segmento ***********d 1* Compi/ado bajo el modelo small */ # include <stdio.h> # include <malloc.h > # include <string.h> struct nodo_arbol [ char clave[20}; nodo _based((_segment).-Self) *izdo; nodo _based((_segment).-Self) *dcho; }; void main(void) [ nodo _based(_segname("MISEG")) *raiz; nodo _based(_segname("MISEG")) *p; raiz = NULL; p = _bmalloc(_segname("MISEG"), sizeof(nodo)); -fstrcpy((char _far *)p->clave, (char _far *) "12ABOO"); p- >izdo = p- >dcho = NULL; raiz = p; printj("Data seg. = %p n': _segname("~ATA"));
  • 522. printf(S'Miseg = %p n'; -----..Segname(HMISEG")); printfrscJave = %lp'; (~har .-far .)&raiz- >clave); printf,., %Fs n'; (char ~ar .)raiz- >clave); printf(Hizdo = %lp n': (nodo ~ar .)&raiz- >izdo); printf(Hdcho = %lp n'; (nodo .-far .)&raiz- >dcho); I Data seg. Miseg clave izdo dcho = 22F2 = 22Fl 22Fl:FFFF 22Fl:0013 22Fl:0015 Cuando trabajamos con punteros near, far, 0 based, necesitamos fundo- nes compatibles con estos, que nos permitan asignar, reasignar, y Iiberar memoria. Estas funciones estan declaradas en el fichero malloc.b, y son las siguientes: ---Jlmalloc _fmalloc _bmalloc ---Jlcalloc _fcalloc _bcalloc _nrealloc ~realloc _brealloc ---Jlfree Jfree _bfree que vimos en el capitulo de "punteros" con la salvedad del atea de memo- ria sobre la que trabajan: memoria near, memoria far, 0 memoria based, 10 cual es trans parente al usuario. Los formatos correspondientes son los siguientes:
  • 523. void _based(void) * _bFealloc(segment seg, void _based(void) *P. size_t t); Esta funcion cambia el tamafto de un bloque de memoria previamente asig- nado, sin modificar su posicion en memoria. Es igual que la funcion real/oc( ), excepto que esta ultima puede modificar la posicion de memo- ria del bloque. Si hay insuficiente espacio de memoria la funcion retorna un puntero nulo (NULL).
  • 524. En un ejemplo anterior, no hemos podido utilizar la funci6n strcpy() por- que esta espera como argumentos punteros near; en su lugar hemos utili· zado la funci6n -!strcpy( ), la cual acepta como argumentos punteros far. Esta es la raz6n de la existencia de las funciones que se enumeran a conti- nuaci6n: _fstrcat _fstrcspn _fstrncpy _fstrtok _fstrnset _fstrchr _fstrlen _fstrrchr _fstrlwr _fstrdup _fstrcmp _fstrncat _fstrspn _fstrupr _fstrcpy _fstrncmp _fstrstr _fstrset Estas funciones, salvando la idea de que han side disefiadas para aceptar argumentos far, tienen el mismo significado que las descritas en el "capi- tulo 5" sin el prefijo _f. Como ejemplo, la funci6n para comparar dos cadenas de caracteres tiene la forma: Las declaraciones para estas funciones se encuentran en el fichero string.h. Las funciones que se describen a continuaci6n trabajan con areas deme· moria (buffers); entendiendo por area de memoria el espacio ocupado por una estructura cualquiera (struct, uni6n, array etc.). Copia cero 0 mas bytes desde un buffer fte a otro des. EI ultimo byteco- piado es el primer byte de fte que coincida con el byte especificado por c, 0 cuando se hayan copiado n bytes; la condici6n que se de primero. # include <string.h > 0 <memory.h > void *memccpy(void *-lies, void 4te, int c, unsigned int n);
  • 525. Esta funcion retorna un puntero al siguiente byte en des si c es copia- do. En otro caso, retorna un puntero nulo (NULL). Busca el byte c, en el buffer buf. La busqueda finaliza cuando el byte c es encontrado 0 cuando se hayan explorado Ios n primeros bytes. Esta fundon retorna un puntero al primer byte que se encuentre en buf En otro caso, si no aparece c retorna un puntero nulo (NULL). Esta funcion compara Ios primeros n bytes de bun y buf2, distinguiendo mayusculas y minusculas, y devuelve un valor: <0 si bun es menor que buf2, = 0 si bun es igual a buf2 y >0 si bun es mayor que buf2 Esta fundon tiene el mismo formato y realiza Ia misma fundon que memcmp(), excepto que no hace distincion entre mayusculas y minusculas.
  • 526. Copia n bytes desde el buffer fte al buffer des. Si los buffers fte y desestan solapados, el resultado es impredecible. Copia n bytes desde el buffer fte al buffer des. Si los buffers fte y desesi~ solapados, el resultado es el esperado. # include <stdio.h > # include <string.h >
  • 527. char fuente[20j = "abcdefghijKLMNO"; char destino[20j; main( ) ! char *P; char :kSolapado = &(fuente[10)); size_t contal = sizeof(fuente); size_t conta2 = strlen(fuente); / * contal = 20 */ / * conta2 = 15 */ printj("Buffer inicial para todas las operaciones: n"); printj("%s n n': fuente); printj("Despues de ejecutarse la funci6n indicada n n "); / * Buscar un cardcter */ p = memchr(fuente, 'K: conta2); printj("memchr: buscar K n"); printj("fuente = %s nresultado = %s n n': fuente, p); / * Copiar. No hay solapamiento entre buffers */ memcpy(destino, fuente, conta2); printj("memcpy: sin solapamiento n"); printj("fuente = %s ndestino = %s n n': fuente, destino); / * Copiar. Hay solapamiento entre buffers */ memcpy(solapado, fuente, conta2); solapado[conta2j = ' 0:- printj("memcpy: con solapamiento n "); printj("fuente = %s nsolapado = %s n n': fuente, solapado); / * Rellenar con un cardcter */ memset(destino, ' 0: contal),- / * Restaurar fuente */ memccpy(fuente, "abcdefghijKLMNO': ' 0: contal),- / * Copiar. No hay solapamiento entre buffers */ memmove(destino, fuente, conta2); printf("memmove: sin solapamiento n"); printj("fuente :::;:%s ndestino :::;:%s n n': fuente, destino);
  • 528. / * Copiar. Hay solapamiento entre buffers */ memmove(solapado, fuente, conta2); solapado[conta2] = ' 0'; printf(Hmemmove: con solapamiento n"); printf(Hfuente = %s nsolapado = %s n n': fuente, solapado); } Buffer inicial para todas las operaciones: abcdefghijKLMNO memchr: buscar K fuente = abcdefghijKLMNO resultado = KLMNO memcpy: sin solapamiento fuente = abcdefghijKLMNO destino = abcdefghijKLMNO memcpy: con solapamiento fuente = abcdefghijabcdefghijabcde solapado = abcdefghijabcde memmove: sin solapamiento fuente = abcdefghijKLMNO destino = abcdefghijKLMNO memmove: con solapamiento fuente = abcdefghijabcdefghijKLMNO solapado = abcdefghijKLMNO SOPORTE MS-DOS PARA MANIPULACION DE BLOQUES DE MEMORIA Cuando se trabaja bajo MS-DOS con argumentos far, utilizaremos lassi· guientes versiones de las funciones anteriores:
  • 529. void _far * _far _fmemccpy(void _far *lies, void _far *fte, int c, unsigned int n); int _far _fmemcmp(const void _far *bufl, const void _far *buf2, size_t n); int _far _fmemicmp(const void _far *bufl, const void _far *buf2, size_t n); void _far * _far _fmemcpy(void _far *lies, const void _far *fte, size_t n); void _far * _far _fmemmove(void _far *lies, const void _far *fte, size_t n);
  • 530. Entorno de programaci6n basado en ventanas que incor- pora un editor de textos, un compilador, un enlazador, un depurador, la utilidad Make, un analizador de c6digo fuen- te, y un sistema de ayuda en linea. El paquete Microsoft C incluye varios programas de uso general. Entre ellos estan: Es el programa que controla la compilaci6n. Permite com- pilar y enlazar programas fuente. Microsoft C incluye dos compiladores C separados: el compilador total y el com- pilador rapido. El compilador utilizado por defecto es el compilador to- tal. Para utilizar el compilador rapido hay que especificar la opci6n /qc. El compilador rapido no puede ejecutar much as de las op- timizaciones que permite el compilador total, pero a dife- rencia de este, permite la compilaci6n incremental (opci6n
  • 531. IGi). La compilaci6n incremental aumenta la velocidad de compilaci6n ya que el compilador solamente compila aque- llas funciones que, des de la ultima compilaci6n, han sido modificadas. LINK.EXE Programa utilizado para en lazar ficheros objeto produci- dos por CL Ie con las librerias apropiadas para producir un fichero ejecutable. ILINK.EXE Es el enlazador incremental. El enlace ofrece las mismas ventajas que la opci6n IGi del compilador. IUNK sola- mente enlaza aquellos m6dulos que han cambiado desde el ultimo enlace. Para crear de un programa fuente C un programa ejecutable bajo DOS, los pasos a seguir son: 2. Compilar cad a fichero fuente creando el correspondiente fichero objeto utilizando la orden CL. 3. Se ejecuta la orden LINK para en lazar los ficheros objetos, lasti· brerias especificadas y las librerias del sistema. El resultado esun fichero ejecutable (.exe) Si el programa fuente C esui compuesto por un unico m6dulo, lospa sos 2 y 3 pueden realizarse ambos con la orden CL. Permite com pilar 0 compilar y enlazar uno 0 mas ficheros fuente C. fichero de salida, si no se especifica 10 contrario, tendra el mismo nomb que el primer fichero especificado.
  • 532. CL [opeton]... jiehero [[opeton]... Uiehero]...]... [/link [lib... ope-link]] indica cualquier opci6n de CL. Se utilizan para compilar el program a bajo un as condiciones especificas. Estas afectan a cualquier fichero que aparezca posteriormente en la linea de 6rdenes. Puede verse un resumen de estas opciones ejecutan- do CL /HELP. Las opciones hay que escribirlas tal cual se indican, ya que se hace distinci6n entre mayusculas y mi- nusculas. nombre del fichero fuente a compilar, del fichero objeto a en- lazar 0 de la Iibreria que se desea pasar al proceso de enlace. indica el nombre de la libreria (.Lffi) que se desea pasar al pro- ceso de enlace. indica una 0 mas opciones para el proceso de enlace. Normal- mente estas opciones no son necesarias excepto cuando se re- quiere ejecutar operaciones especiales, como por ejemplo cam- biar el tamafio del stack. Las opciones se escriben a continuaci6n de /link y van precedidas por /. Ver en este mis- mo capitulo la orden LINK. El numero de opciones, nombres de ficheros y nombres de librerias puede ser cualquiera con la unica limitaci6n de que la linea de 6rdenes no puede exceder de 128 caracteres. En este ejemplo la orden CL ejecuta las acciones de compilar y enla- zar bajo las especificaciones del modele grande de memoria. Los ficheros fuente .e, son compilados y los ficheros objeto .ob} y librerias .lib especifi- cados son enlazados. Este proceso es descrito a continuaci6n e ilustrado con la figura que se muestra:
  • 533. 1. CL compila Ios ficheros fuente especificados creando ficheros ob- jeto (.OBJ). 2. CL llama al programa enlazador (link) y Ie pasa Ios ficheros obje- to resultantes de Ia compilaci6n, mas Ios especificados, mas las librerias. 3. LINK enlaza estos ficheros objeto, Ias librerias especificadas y las librerias del sistema, y crea un fichero ejecutable (a.exe en el ejemplo). C E B-- 0 D M I P I T L 0 A R D 0 R c.obj E N L A Z A • D o R 0-- En un nombre de fichero no se hace distinci6n entre maytisculas y mi· miscula,:. Cualquier nombre de fichero puede especificarse mediante elca· mino correspondiente.
  • 534. Este ejemplo compila a.c, creando a.ob} y pasa a.ob}, b.ob} y graphics. lib, al programa enlazador para crear el fichero ejecutable a.exe. Cuando no se especifica el camino para un fichero, CL asume que este se encuentra en el directorio actual de trabajo. Sino se tiene la seguridad de que el programa coja en la memoria dis- ponible,podemos crear overlays. Un overlay es una parte del programa (m6- dulo)almacenada sobre el disco; la cual sera cargada en memoria cuando serequiera su ejecuci6n. Para especificar que un determinado m6dulo se va a tratar como un overlay, se incluye entre parentesis. Este ejemplo crea un program a Hamado modJ.exe con un overlay de- nominado mod2. Cuando durante la ejecuci6n, el control pase a mod2, este sera cargado en memoria y ejecutado. Un identificador de fichero bajo DOS consta de dos partes: un nombre de hasta 8 caracteres y una extension de hasta 3 caracteres separada del nombre par un pun to. Significado CL asume el fichero como un fichero fuente C y 10 compila. CL asume el fichero como un fichero objeto y 10 pasa al programa enlazador (linker).
  • 535. CL asume el fichero como una libreria y 10 pasa al pro- grama enlazador. CL asume el fichero como un fichero objeto y 10 pasaal programa enlazador. Cuando el nombre del fichero no ten· ga extensi6n, este debe finalizarse con un punto, de 10 con- trario, CL asumini la extensi6n .OBJ. Las opciones comienzan con el canicter "I" 0 con el caracter "." y deben utilizarse exactamente igual que se describen, esto es, con la misma combi· naci6n de letras maytisculas y minusculas. A continuaci6n se describen las opciones para la orden CL agrupadas segun la funci6n que desempefian. instrucciones 8086. Opci6n por defecto instrucciones 80186 instrucciones 80286 convenio -fortran 0 -pascal de Hamada a funciones convenio estandar _cdecl de Hamada a funciones. Opci6n por defecto. permite el chequeo de la pila. Opci6n por defecto compilaci6n incremental. Implica ILL Utilizar junto con la opci6n Iqc. almacena las cadenas en el segmento CONST. No dispo· nible con la opci6n Iqc /00 101 102 10c 10d 10e 10i
  • 536. /Gs /Gt[num] convenio -fastcall de Hamada a funciones. Cuando es po- sible, los valores son pasados a los registros de la UCP en lugar de hacerlo sobre la pHa. se suprime el chequeo de la pila coloca elementos de datos mayores 0 iguales que num bytes en un nuevo segmento. Por defecto num es 256. S610 se puede utilizar con los modelos compacto, grande y enor- me. Es util para programas que tienen mas de 6~K en pe- quefios elementos de datos estaticos y glob ales inicia- lizados. c6digo de entrada/salida para utilizar en aplicaciones'bajo Microsoft Windows. igual que /Gw, pero genera c6digo de entrada mas eficiente. selecciona la libreria matematica alternativa. No disponible con la opci6n /qc selecciona la libreria para emular el 80x87 si este no est a presente. No disponible con la opci6n /qc selecciona la libreria 80x87. Requiere un coprocesador 80x87. No disponible con la opci6n /qc genera instrucciones en linea para un coprocesador 80x87 y selecciona la libreria para emular el 80x87 si este no esta presente. Es la opci6n por defecto. genera instrucciones en linea para un 80x87 y selecciona la libreria 80x87. Requiere coprocesador.
  • 537. IZa IZc IZd IZe IZg IZi IZI IZp[num] IZr IZsjich compatibilidad ANSI. No permite extensiones Microsoft ignora mayusculas/minusculas para cualquier nombre de- clarado con ~ascal. No disponible con la opdon /qc genera informacion requerida por el depurador SYMDEB permite extensiones Microsoft genera funciones prototipo genera informacion requerida por el depurador Code View sup rime informacion de libreria por defecto empaqueta los miembros de estructuras y uniones (num es 1, 2 0 4) permite el chequeo de punteros (utilizar solo con /qc) ejecuta sobre el fichero fuente especificado un chequeo s610 de la sintaxis IF bytes IHnum llink IVstring permite especificar el tamafio de la pila en hexadecimal numero de caracteres significativos para un nombre. Por defecto es 31 para crear un fichero ejecutable en un modo compatible (OS/2) invoca al enlazador incremental IUNK. Opcionalmente se pueden afiadir durante el enlace un total de num bytes de relleno, con el fin de preyer espacio para pequefias mOo dificaciones. especificar opciones 0 librerias para el enlazador copia la version string en el fichero objeto
  • 538. lAC IAH IAL lAM IAS IAT utiliza el modelo compacta (compact) utiliza el modelo enorme (huge) utiliza el modelo grande (large) utiliza el modelo medio (medium) utiliza el modelo pequeno (small) utiliza el modelo pequenito (tiny) 10 lOa 10d 10e 109 10i 101 lOp 10s lOt lOw velocidad de ejecuci6n mayor. Igual que lOt no "alias" (varios nombres para referirse a una posici6n de memoria) eliminaci6n, a nivel de bloque, de subexpresiones identi- cas con el fin de que su evaluaci6n se produzca una sola vez no permite optimizaciones utilizaci6n de los registros de la UCP para colocar las va- riables y subexpresiones que se utilizan con mas frecuen- cia igual que 10c, pero a nivel de funci6n genera funciones intrinsecas (mas rapidas) actua sobre bucles para que se ejecuten mas rapidos evitar las diferencias en precisi6n cuando se trabaja con valores reales de tipos diferentes ya que pueden afectar a las operaciones de relaci6n. tamano mas pequeno del c6digo generado velocidad de ejecuci6n mayor. Opci6n por defecto no "alias" excepto en llamadas a funciones. No disponi- ble con la opci6n Iqc
  • 539. lOx 10z optimizacion equivalente a IOecilgt/Gs optimizacion maxima sobre bucles y utilizacio de 10s registros de la UCP I Fejich IFl[fich] I Fm[fich] I Fojich IFR[fich] genera un listado en ASM (fichero .asm). No disponible con la opcion Iqc genera un listado combinado fuente/asm (fie No disponible con la opcion Iqc nombra el fichero ejecutable (.exe) genera un listado en codigo objeto (fichero . No disponible con la opcion Iqc genera un fichero .map (mapa de enlace) nombra el fichero objeto (.obj) genera un fichero extendido .sbr para el analis go fuente (source browser) genera un fichero estandar .sbr para el analisis fuente (PWB source browser) genera un listado del codigo fuente (fichero No disponible con la opcion Iqc genera un fichero .erf (referencias cruzadas)
  • 540. IEP IIdir IP IUid lu Ix hace que los comentarios no sean suprimidos. Es valida solamente con las opciones IE, IP y IEP define una constante simb6lica para el preprocesador. El valor de id es v. El valor de id puede ser vacio (lDid=) o 1 (lDid) copia la salida producida por el preprocesadoT en la sali- da estandar, insertando la directriz # line. Esta opci6n suprime la compilaci6n. igual que IE pero no inserta la directriz # line afiade el directorio dir a la lista de directorios donde de- ben buscarse los ficheros .h igual que IE,pero la salida se copia en un fichero .i borra la definici6n del identificador id especificado borra todos los identificadores predefinidos ignora la variable INCLUDE= ISlnum ISpnum ISscad IStcad define el nOde caracteres por linea (por defecto 79) define el nO de lineas por pagina (por defecto 63) especifica un subtitulo para el listado fuente especifica un titulo para el listado fuente invoca a CIL.EXE (paso 1 del compilador modelo gran- de). path especifica el camino del fichero CIL. Utilizar esta opci6n para compilar programas que generan el error' 'out of near heap space"
  • 541. IB2path IB3path IBATCH Ic IHELP IJ IMAopc Inologo Iqc IW IWO IWn invoca a C2L.EXE (OS/2) invoca a C3L.EXE (OS/2) suprime la salida al ejecutar ficheros .bat solo compila, no enlaza peticion de ayuda en Microsoft C el tipo char es signed por defecto. Estaop- cion 10 cambia a unsigned, excepto cuando un valor ha sido declarado explicitamente signed pasa la opcion ope especificada al MASM suprime el logotipo (informacion inicial) compilacion nlpida. Conjuntamente con esta opci6n se pueden utilizar las opciones IGi (implica ILi) y /Zr. No se pueden utilizar las opciones: lOw, IGm, IFa, IFc, IFl, IFs, IFPa, IFPc, IFPc87, IHnum y IZc indica que jich es un fichero .asm independientemente de su extension indica que jich es un fichero .c independientemente desu extension igual que IW1. Es la opcion por defecto suprime los mensajes de aviso del compilador (warnings) indica el nivel de mensajes de error a visualizar. n esun valor 1, 2, 3, 0 4. A mayor nivel, mayor exigencia en el amilisis sintactico durante la compilacion es el mayor nivel de mensajes de error. Cualquier avisoes considerado como un error fatal. igual que WO sintaxis de la orden emitida (ejemplo cl 17) Esta orden permite enlazar el fichero objeto obtenido como resultado de una compilacion, con las librerias apropiadas, para crear un fichero ejecu- table. La sintaxis de esta orden es la siguiente:
  • 542. indica uno 0 mas ficheros objetos que se quieren enlazar. Los nombres de los ficheros objetos son separados por el signo + o por espacios. La extension asumida por omision es .obj. es el nombre del fichero ejecutable que se quiere crear. La ex- tension asumida por omision es .exe. es el nombre del fichero 0 mapa de carga, el cual contiene una entrada para cada segmento de los modulos de entrada. Cada una de las entradas muestra tambit~n el desplazamiento dentro del fichero ejecutable. Este fichero no se crea a menos que se solicite especificamente. Si no se introduce nada el nombre oe fichero reservado es NUL que indica que no se creara ningun mapa. Se puede especificar tambien un nombre de dispositivo para di- rigir la salida a este, como por ejemplo: AUX para un dispositi- vo auxiliar, CON para la consola (terminal), PRN para la im- presora. lib indica una 0 mas librerias (stand-alone) 0 directorios donde pue- den ser localizadas separadas por signos + 0 por espacios. opciones pueden ser cualquiera de las siguientes (la parte incluida entre corchetes es opcional): IBA IBA[TCH] Suprime la salida por pantalla de mensajes, dellogotipo, de pre- guntas 0 de las lineas de un fichero .bat. Ica ICO[DEVIEW] Prepara un fichero ejecutable para depurarlo utilizando el de- purador de Microsoft Code View. Los ficheros objeto enlaza- dos con esta opcion deben compilarse primero con la opcion IZi. ICP ICP[ARMAXALLOC]:num Maximo numero de parrafos de 16bytes necesarios para cargar el programa en memoria. Por defecto DOS asigna el maximo espacio disponible no quedando espacio para ejecutar otro programa.
  • 543. DO IDO[SSEG] Solamente para programas en ensamblador. Los segmentos son ordenados en el fichero ejecutable en un orden preestablecido. Opci6n por defecto. IDS IDS [ALLOCATE] Solamente para programas en ensamblador. Carga todos los da- tos definidos para estar en la parte mas alta del segmento de datos. Por defecto, link carga los datos comenzando por la par- te mas baja del segmento de datos. Esta opci6n es utilizada nor- malmente con la opci6n IHI. IE IE [XEPACK] esta opci6n elimina secuencias repetidas de bytes (generalmen- te caracteres nulos) y optimiza la tabla de carga, tabla de refe- rencias relativas al principio del programa. Los ficheros .exeerea- dos con esta opci6n pueden ser mas pequefios y cargados mas rapidamente. '.' I f", ., ,-;~t~;Y •••••• u,tI IF [ARCALLTRANSLATION] Puede mejorar la velocidad y reducir el tamafio de los progra- mas. Convierte las llamadas -far a funciones que residen en el mismo segmento, a llamadas _near. Normalmente se utiliza con la opci6n IPACKCODE. IHE[LP] Petici6n de ayuda. IHI[GH] hace que el cargador coloque el fichero ejecutable 10 mas alto po sible en la memoria. Esta opci6n se utiliza normalmente con la opci6n IDS. IINC[REMENTAL] Invoca al compilador incremental IUNK. No es compatible con las opciones IE y ITINY. Con esta opci6n se generan dos fi- cheros mas: .sym y .i1k, necesarios para IUNK. IINF[ORMATION] Visualiza la fase de enlace y los nombres de los ficheros objetos que estan siendo enlazados.
  • 544. ILl [NENUMBERS] Crea un fichero .map que incluye los numeros de linea del fi- chero fuente y las direcciones asociadas. Para ello es necesario que el fichero 0 ficheros objeto incluyan numeros de linea (op- ciones del compilador IZi 0 IZd), de 10 contrario la opcion ILl sera ignorada. IM[AP] Crea un fichero que contiene una lista clasificada de todos los simbolos globales definidos en el fichero 0 ficheros objeto, ade- mas de la lista de los segmentos del programa en su orden de aparicion en el modulo de carga. INOD [EFAULTLlBRARYSEARCH] [:lib] Ignora las librerias por defecto. Si se especifica lib, LlNK bus- ca en todas las librerias excepto en esta. INOE[XTDICTIONARY] esta opcion previene al programa enlazador de buscar en el dic- cionario que el mismo mantiene. Normalmente el enlazador con- sulta este diccionario (lista de localizaciones de simbolos) para acelerar la busqueda de librerias. La opcion INOE la utilizare- mos cuando exista una redefinicion de un simbolo. INOF[ARCALLTRANSLATION] Si la conversion de llamadas -far a llamadas _near esta acti- va, entonces se desactiva. (Ver opcion IF). INOI[GNORECASE] hace que el enlazador distinga entre letras mayusculas y mi- nusculas. INOL[OGO] Suprime el logotipo. INON[ULLSDOSSEG] Clasificar los segmentos en el fichero ejecutable en el mismo orden que 10 hace la opcion IDO, pero sin bytes adicionales al comienzo del segmento _TEXT.
  • 545. ISE ISE[GMENTS]:num Indica el numero maximo de segmentos 16gicos que un progra· ma puede tener (por defecto 128). num es un valor de 1 a 16384. INOP INOP[ACKCODE] Si el empaquetamiento de los segmentos de c6digo esta activo, se desactiva. 10 10 [VERLAYINTERRUPT] :number Permite seleccionar un numero de interrupci6n diferente aI 63 (numero por defecto) para pasar el control a overlays. IPACKC IPACKqODE][:num] Empaqueta los segmentos de c6digo en los modelos de memo- ria medio, grande y enorme. num indica el tamafio maximo para el nuevo segmento (por defecto 64K). Cuando LINK no pueda afiadir otro segmento por superar el valor num forma otro nue- vo. Esta opci6n se utiliza junto con la opci6n IF. IPACKD IPACKD[ATA][:num] Empaqueta los segmentos de datos en los modelos de memoria compacto, grande y enorme. IPADC IPADqODE]:num Afiade bytes de relleno al final de cada m6dulo de c6digo para posteriormente enlazarlos con ILINK. Esto es necesario para prever espacio para pequefias modificaciones. num especifica el numero de bytes de relleno (por defecto 40). IPADD IPADD[ATA]:num Afiade bytes de relleno en cada segmento de datos. IPAU IPAU[SE] indica al enlazador que haga una pausa antes de escribir eIfi· chero .exe en un disco flexible. Esto per mite insertar un nuevo disco para almacenar el fichero .exe. IQ IQ[UICKLIB] Produce una libreria Quick para titilizarla con Microsoft QuickC o Microsoft QuickBASIC.
  • 546. Un numero bajo de segmentos permite un enlace mas rapido y una asignaci6n de espacio menor. 1ST IST[ACK]:num especifica el tamafto de la pila para el programa. num es cual- quier valor positivo (decimal, octal 0 hexadecimal) no superior a 65535. El tamafto por defecto es de 2K. IT IT[lNY] Indica al enlazador que se va a crear un fichero .com. Esta op- ci6n es incompatible con la opci6n IINCREMENTAL. Este ejemplo enlaza los ficheros prog.ob}, modI.ob} y mod2.ob}, con las librerias por defecto (librerias del sistema). El resultado es el fichero ejecutable prog.exe, almacenado en el directorio source y optimizado en tamafto y velocidad (opciones IE y IF). Tambien se crea el mapa de carga, prog.map, correspondiente. En el siguiente ejemplo, se enlazan los ficheros progJ.ob}, prog2.ob} y prog3.ob} utilizando la libreria libutil.lib. El resultado es el fichero deno- minado progI.exe por defecto. El mapa de carga correspondiente es Hama- do mapaen.map. Una segunda forma de enlazar uno 0 mas programas es ejecutando la or- den LINK sin parametros:
  • 547. Object Modules [.OBJ]: Run File [nombre-base.EXE]: List File [NUL.MAP]: Libraries [.LIB]: Definitions File [NUL.DEF]: La primera pregunta se responde con el nombre del programa 0 pro- gramas a enlazar separados por signos + 0 por espacios. Se asume la ex- tension .obj. A la segunda pregunta se responde con Enter a no ser que deseemos cambiar el nombre para el fichero ejecutable. Si deseamos obte- ner un listado del mapa de carga (fichero .map), debemos responder a la tercera pregunta con un nombre diferente a NUL; la extension .map se asume. A la cuarta pregunta se responde con una 0 mas librerias (stand-alone) 0 con nombres de directorios don de pueden ser localizadas, separadas par signos + 0 por espacios. Por omision se asumen las librerias del sistema. La quinta pregunta solamente es requerida par OS/2 y Windows, por 10 que simplemente pulse Enter. Si hay mas nombres que los que entran en una linea, se pone un signa + al final de esta y se continua en la linea siguiente. El siguiente ejemplo ilustra como continuar en la linea siguiente utili- zando el signo +. Este ejemplo enlaza los ficheros .obj especificados y crea un fichero .exe. Por responder con un punto y coma a la pregunta Run File, el fichero ejecutable es nombrado par defecto progl y se asumen la res- puesta NUL.MAP y las librerias .LIB del sistema para las dos preguntas siguientes. Object Modules [.OBJ]: progl prog2 prog3 + Object Modules [.OBJ]: prog4 Run File [PROGl.EXE]: ; <Enter> A menudo es conveniente salvar las respuestas alas preguntas formulada por la ejecucion de la orden LINK para utilizarlas posteriormente. Esto
  • 548. es especialmente uti! cuando hay que especificar listas largas de m6dulos objeto y ademas se preve, por los motivos que sean, que el proceso de en- lace, seguramente, habra que realizarlo mas de una vez. Antes de utilizar esta opci6n debe crearse un fichero de respuesta auto- matica. Este contiene varias lineas de texto, cada una de las cuales es la respuestaa una pregunta del enlazador. Estas respuestas deben ir en el mismo orden que las preguntas del enlazador y siguen las mismas reglas expuestas anteriormente. Para especificar un fichero de respuesta automatica se procedera como se indica a continuaci6n: a+b+c+d /PAUSE IMAP list xlib.lib; Esta orden Ie indica al programa enlazador que procese los ficheros objeto a, b, c y d para producir un fichero ejecutable Hamado a.exe y un mapa list.map. La opci6n IPAUSE indica a LINK que haga una pausa an- tes de escribir el fichero ejecutable para poder cambiar el disco flexible, siesnecesario. La opci6n IMAP indica que se cree el mapa de carga para el fichero ejecutable resultante de la operaci6n de enlace. Tambien se pro- duce el enlace con las rutinas necesarias de la libreria x/ib./ib. Sino se tiene la seguridad de que el programa coja en la memoria disponi- blepodemos crear overlays. Un overlay es una parte del programa (m6du-
  • 549. 10) almacenada sobre el disco; la cual sera cargada en memoria cuando se requiera su ejecucion. Para especificar que un determinado modulo seva a tratar como un overlay se incluye entre parentesis. Este ejemplo crea prog.exe en el directorio actual de trabajo con dOl overlays: (modl+mod2) y (mod3). La utili dad IUNK (enlazador incremental) normalmente es invocada por los compiladores QuickC y PWB basados, por defecto, en la compilaci6n y enlace incremental (opciones IGi 0 ILi). No obstante, IUNK tambien puede ejecutarse directamente desde DOS. Para poder ejecutar IUNK previamente debe utilizarse, al menos una vez, la orden LINK con las opciones IINC, IPADC y IPADD; ya que lUNK trabaja sobre un fichero .exe existente (no .com), el cual desearn os modificar. fich.exe es el nombre del fichero ejecutable que se quiere crear. La ex- tension asumida por omision es .exe. objeto indica uno 0 mas ficheros objetos que se quieren enlazar sin la extension .obj y separados por espacios. IA indica a IUNK que verifique si han ocurrido cambios en 101 ficheros .obj desde el ultimo proceso de enlace.
  • 550. IE "orden!; orden]..." inqica la orden u 6rdenes que deben ser ejecutadas si ILINK falla. La opd6n por defecto es IE "LINK IINC". si ocurre un fallo al ejecutar ILINK, por defecto, no se invoca LINK lINe. IX si esta disponible un manejador de memoria expandida, esta opd6n hace que ILINK no 10 utilice. Por defecto, ILINK uti- liza memoria expandida.
  • 551. LIBRERIAS Y UTILIDADES DEL COMPILADOR Una de las caracteristicas mas importantes de C es la facilidad con la que se pueden construir y utilizar librerias de funciones. Una libreria es una coleccion de funciones precompiladas y agrupadas bajo un mismo nom- bre(fichero) que pueden ser utilizadas por cualquier programa. La ventaja de utilizar tales librerias esta en no tener que escribir el codigo fuente de la funcion (de libreria) en el programa y en el tiempo que se ahorra, ya queel compilador cuando compila nuestro programa no tiene que compi- lar tales funciones. C proporciona la utilidad LIB para crear librerias que llevan como ex- tension.LIB y para mantener las ya existentes. Estas son denominadas tam- bien librerias stand-alone 0 librerias independientes (cuando son requeri- das el sistema las llama automaticamente). Estas librerias tienen el mismo formate que las suministradas con C y con otros lenguajes de alto nivel de Microsoft. En este capitulo, ademas de las librerias, veremos otras utilidades como NMAKE y Code View por citar algunas de ellas.
  • 552. Si se escribe un punta y coma (;) a continuacion del nombre de la libreria, Lffi ejecuta solamente un chequeo sobre la libreria especificada. Si se encuentra algun modulo invalido se escribe un mensaje de error. Este chequeo normal mente no es necesa- rio, ya que Lffi cheque a cada fichero objeto antes de afiadirlo a la libreria. Por ejemplo: EI manejador de librerias Lffi permite crear y mantener librerias .LIB. Un programa que llama a rutinas de una libreria es enlazado (link) con la Ii- breria para producir el fichero ejecutable. Solamente son enlazadas las ru- tinas necesarias, no todas las rutinas de la libreria. Una libreria stand-alone esta hecha a base de modulos objeto que han sido combinados para formar la misma. A diferencia de un fichero objeto, un modulo objeto no existe independientemente de la libreria a la que per- tenece y no tiene un camino 0 extension asociada con su nombre. • Extraer modulos de una libreria existente y colocarlos como fiche- ros .obj independientes. • Combinar el contenido de dos librerias existentes para formar una unica libreria. Iib-vieja indica el nombre de la libreria que se desea crear 0 modificar. Si la libreria no existe, LIB pregunta si se quiere crear. Por de- fecto se asume la extension .LIB.
  • 553. IH[ELP] peticion de ayuda. II [GNORECASE] indica a LIB, cuando compara nombres, que no haga distincion entre minusculas y mayusculas. Utilizar esta opcion cuando se combine una libreria creada con la opcion INOI, con otras que no han sido creadas con esta opcion. La libreria resultante no es marcada INOI. Es la opcion por defecto. INOE[XTDICTIONARY] indica a LIB que no genere un diccionario. Este es una parte ex- tra de la libreria que mas tarde dara lugar a una mayor veloci- dad en el proceso de enlace de la misma. Utilizar esta opcion cuando se presente el error out of memory. Otra posibilidad es dividir la libreria en otras mas pequefias. INOI[GNORECASE] indica a LIB, cuando compara nombres, que distinga minuscu- las y mayusculas. Utilizando esta opcion, nombres como fUDcA y fUDca pueden ser puestos en la misma libreria. Cuando se crea una libreria con esta opcion, LIB marca la libre- ria internamente para indicarlo. Cuando se combinan varias 1- brerias y una de ellas esta marcada INOI, la libreria resultante es marcada tambien INOI. INOL[OGO] suprime el logotipo en la salida. IPAGESIZE:n especifica el tamafio de la pagina para la libreria. n es una po- tencia entera de 2 cuyo valor esta comprendido en el rango de 16 a 32768. Por defecto el tamafio de la pagina es de 16 bytes. El tamafio de la pagina afecta a la alineacion de los modulos en la libreria. Los modulos en la libreria son siempre alineados
  • 554. para comenzar en una posicion que sea un multiplo del tamafio de la pagina. + {fich-obj/lib J afiade a la libreria el fichero objeto especificado. Si la ex- tension no se especifica se asume .obj. Si se especifica una libreria, su contenido es afiadido a la libreria Iib-vieja. El nombre de la libreria debe tener la extension .lib. -+modulo reemplaza en la libreria el modulo especificado borran· do el modulo y sustituyendolo por el fichero objeto del mismo nombre. *modulo copia el modulo especificado a un fichero objeto del mis- mo nombre. -*modulo mueve el modulo especificado a un fichero objeto del mis· mo nombre. nombre del fichero de referencias cruzadas. Si no se especifica no se crea. Este fichero contiene: 1. Una lista alfabetica de todos los simbolos globales en la Ii- breria. Cada simbolo va seguido por el nombre del modulo en el que esta contenido. 2. Una lista de los modulos de la libreria. Esta lista incluyeel nombre, el desplazamiento en el fichero, y el tamafio de cada modulo. El nombre de cada modulo va seguido por una lis· ta alfabetica de los simbolos glob ales definidos en el mismo. lib-nueva es el nombre de la libreria modificada que LIB crea como sali· da. Cuando se especifica esta opcion la libreria original perma· nece sin modificar. Si no se especifica se crea una copia de segu·
  • 555. ridad de la libreria original con extensi6n .bak, permaneciendo con el mismo nombre la modificada. Reemplazar el m6dulo fund en la libreria lib_a. lib por el fichero ob- jeto del mismo nombre funcl.ob}. EI mismo resultado se obtendria con cualquiera de las dos 6rdenes si- guientes,ya que las operaciones de borrado se efectuan siempre antes que las operaciones de anadir, indiferentemente del orden en el que se hayan especificado en la linea de 6rdenes. EI siguiente ejemplo mueve el m6dulo fund de la libreria lib_a. lib a un fichero objeto denominado fund.ob}, con 10 que este modulo desa- parecede la libreria. Copia el m6dulo func2 a un fichero objeto denomi- nadofunc2.ob}, este m6dulo permanece en la libreria. La libreria modifi- cadaes Hamada lib_b. lib permaneciendo sin cambiar la libreria lib_a. lib. LIB lib_a -fund +fund; LIB lib_a +fund -fund; Library Name: Library does not exist. Create? (y/n) Operations:
  • 556. List File: Output .library: La segunda pregunta s610nos es formulada cuando no existe la libre- ria especificada. La quinta s610nos es formulada si existe la libreria espe- cificada. Si a alguna de estas preguntas, excepto a la primera, se responde con Enter, se asume para ella la respuesta por defecto y si se responde con un punto y coma se asume para ella y para las siguientes las respuestas por defecto. La respuesta por defecto para List File es NUL y para Output library es la especificada en Library Name. Igual que se indic6 para la orden LINK, para la orden LIB se puede crear un fichero de respuesta automatica y especificar la orden: Creamos el fichero res.Ibr con las siguientes respuestas, suponiendo que deseamos crear la libreria especificada: milib y +mayor +cambiar
  • 557. El siguiente ejempl0 crea una libreria Hamada milib.lib con 10s m6dulos mayor y cambiar correspondientes a 10sficheros mayor.obj y cambiar.obj. Los pasos a seguir son 10s siguientes: /* Mayor de x, y, Z */ float mayor(f1oat x, float y, float z) [ float ma; if(x>y) if ( x > z) ma x; else ma = z; else if (y > z) ma = y; else ma = z; return ma; / * Perm uta 10s valores de x e y */ void cambiar(f1oat ~, float *y) [ float awe; aux = ~; ~ *y; *y = awe;
  • 558. Library name: milib library does not exit. Create? (y/n) y Operations: +mayor +cambiar List file: ; 1* miprog.c *1 # iclude <stdio.h > # include Hmisjuncs.h" main( ) ( float a, b = 1, c = 2; a = mayor(l, 2, 3); cambiar(&b, &c); printj(H%g %g %g': a, b, c); } float mayor(f1oat, float, float); void cambiar(f1oat *, float *); 7. Compilar miprog.c y enlazarlo can la libreria milib.lib para obtener el programa ejecutable, miprog.exe.
  • 559. NMAKE es una utilidad que automatiza el mantenimiento de programas. En lugar de escribir explicitamente las ordenes de compilar y enlazar, es posible escribir todos 10s pasos necesarios para obtener un fichero ejecuta- ble en un fichero de texto denominado por defecto makefile, que sera in- vocado mediante la orden NMAKE. Esto es particularmente util para cons- truir programas desde varios modulos fuente. NMAKE solo recompilaria los modulos fuente que hayan sufrido modificaciones en base a la fecha de la ultima vez que se modifieD el fichero, 10 cual exige introducir la fecha correctamente al iniciar cada sesion. Cuando se ejecuta NMAKE sin un nombre de fichero, se busca uno deno- minado makefile. Si no existe, hay que especificar explicitamente el fichero que deseamos utilizar como argumento. El fichero al que nos referimos, "fichero de descripciones", describe las acciones necesarias para construir un determinado programa. Consta de uno 0 mas bloques y cad a bloque esta formado por una linea de dependencia seguida por una 0 mas Iineas de ordenes. El formate es el siguiente: fich-'esultante ... :[[path; ... }][fichl_dependiente .. .][,·orden][#comentario} [ordenl} [# comentario] [# comentario] I [orden2] La primera linea (linea de dependencia) tiene que comenzar en la co- lumna 1 y el sangrado de al menos un espacio en las line as de ordenes es necesario. En ella se especifican los jicheros resultantes y 10s fieheros de los que estos dependen, jicheros dependientes. Opcionalmente se pueden especificar los caminos de cada uno de losjicheros dependientes entre lla- ves y separados por puntos y comas. Un fichero es dependiente si cuando cambia causa una modificacion en 10s ficheros que se derivan de el.
  • 560. Una orden es cualquier expresi6n valida que pueda ejecutarse desde DOS. Hay tres modificadores especiales que pueden utilizarse como prefi- jo de cualquier orden: ignora un posible error en la ejecuci6n de la orden. NMAKE conti- mla el proceso y no se detiene. repite la orden para cada fichero dependiente si dicha orden utiliza alguna de las macros especiales $(?) o· $(**). Un fichero de descripciones puede ser documentado con comentarios. Un comentario aparecera como: NMAKE ignora el texto que hay des de el simbolo # hasta el final de la linea. Un comentario puede colocarse solo en una linea a partir de cual- quier columna, excepto en la secci6n de lineas de 6rdenes que debe comen- zar en la columna 1; al final de cualquier linea del fichero, excepto en las lineas de 6rdenes 0 a partir de la columna 1 de una linea de 6rdenes sepa- rado de la orden por el caracter I (ASCII 124). # progl60J.mak progI60J.exe: progl601.obj # Linea de dependencia # Linea de 6rdenes # Enlace I CL -nologo progl60J.obj progI60J.obj: progl60J.c # Linea de dependencia # Linea de 6rdenes # Compilaci6n I CL -c -nologo progl60J.c primero chequea si la fecha de progl60J.c es mas reciente que prog1601.obj. Si es mas reciente, 0 si progl60J.obj no existe, ejecuta la orden:
  • 561. Esta orden crea un fichero progl601.obj nuevo, el cual es ahora mas reciente que progI60J.exe, 10 que hace que se ejecute la orden: Observar que la construcci6n del fichero de 6rdenes parte siempre del fichero resultante. A continuaci6n presentamos el contenido de un fichero miprog.mak que construye el fichero miprog.exe a partir de los m6dulos fuente: miprog.c, mayor.c y cambiar.c descritos en el apartado anterior. # miprog.mak miprog.exe: miprog.obj mayor.obj cambiar.obj # bloque de enlace CL miprog.obj mayor.obj cambiar.obj miprog.obj: miprog.c # compi/ar CL -c -nologo miprog.c mayor.obj: mayor.c # compi/ar CL -c -nologo mayor.c Cambiar.obj: cambiar.c # compi/ar CL -c -nologo cambiar.c opciones se utilizan para ejecutar NMAKE bajo unas condiciones espe- cificas.
  • 562. macro_definiciones lista de definiciones de macros. Las definiciones de las macros que contengan espacios en blanco han de incluirse entre comi· llas dobles. ficheros_resultantes lista de 10s ficheros a construir. Si no se especifica, NMAKE construye el primer fichero resultante especificado en el fiche- ro de descripciones. IA construye todos los ficheros resultantes aunque los ficheros de- pendientes hayan sufrido modificaciones, 0 no. Ie suprime los mensajes de derechos de co pia, errores no fatales yavisos. visualiza las 6rdenes a ejecutar por NMAKE pero sin ejecu tarlas. Se utiliza como chequeo. no permite la redefinici6n de macros heredadas. NMAKE he· reda todas las variables de entorno como macros 10 que puede causar redefiniciones en el fichero de descripciones. indica el nombre del fichero de descripciones a utilizar. Si no se especifica se supone el fichero makefile. ignora el c6digo de salida (error level). Si no se especifica, cual- quier programa que retorne un c6digo distinto de cera, provo- ca la finalizaci6n de NMAKE:
  • 563. visualiza todas las macros, reglas de inferencia y ficheros re- sultantes. chequea si hay ficheros que han variado respecto al fichero re- sultante; en caso afirmativo retorna un valor distinto de cero. En caso contrario devueve un O. suprime la visualizaci6n de las 6rdenes del fichero de descrip- ciones cuando son ejecutadas. cambia las fechas de los ficheros que han variado a la fecha actual. Las macros se utilizan para crear un fichero de descripciones valida para varios proyectos. Por ejemplo las macros pueden representar nombres de ficheros en 6rdenes. Esos nombres pueden entonces ser definidos cuando se ejecute NMAKE. Las macros pueden tambien utilizarse para controlar las opciones que NMAKE pase al compilador y al enlazador. Una macro se crea de la forma siguiente: Para utilizar una macro en un fichero de descripciones emplearemos la siguiente sintaxis: Si el nombre de la macro es de un s610caracter 10sparentesis no son necesarios.
  • 564. FUENTES = MODJ.C MOD2.C MOD3.C PROG.EXE: $(FUENTES: .C=.QBJ) # /{nea de dependencia LINK $**; Este ejemplo describe una macro Hamada FUENTES. Con esta ma- cro sustituimos en la linea de dependencia la extension .C por la extension .OBJ. De este modo NMAKE ejecuta la siguiente orden: NMAKE provee varias macros predefinidas. Algunas de eHas son las si- guientes: nombre del fichero resultante que NMAKE esta procesan- do. Esta macro solamente es utilizada en la linea de depen- dencia para especificar una dependencia.
  • 565. lista de los ficheros dependientes que han variado con res- pecto al fichero resultante. fichero dependiente que ha variado con respecto al fichero resultante. Esta macro solamente es utilizada en reglas de inferencia. orden utilizada para invocar al compilador Microsoft C. Por defecto CC = CL. orden utilizada para invocar al Macro Ensamblador. Por defecto AS = MASM. orden utilizada para invocar recursivamente a NMAKE. Por defecto MAKE = NMAKE. $(MAKEFLAGS) opci6n de NMAKE actualmente en efecto. Para invocar re- cursivamente a NMAKE utilizar: $(MAKE) -$(MAKEFLAGS). MILIB.LIB: MODl.oBJ MOD2.oBJ MOD3.oBJ !LIB MILIB.LIB -+$?,o La macro $? representa el nombre de todos los ficheros dependientes que han variado con respecto al fichero resultante. EI simbolo !que prece- de a la orden LIB hace que NMAKE ejecute la orden LIB una vez por cada fichero dependiente que ha variado. Los caracteres D, F, By R pueden modificar el valor de algunas de las ma- cros anteriores segun se indica a continuaci6n:
  • 566. DIR = C: C600 INCLUDE $(DIR) GWBALS.H $(DIR) TYPES.H $(DIR) MACROS.H· $$(@F) COpy $? $@ La macro especial $$(@F) indica el nombre (sin path) del ficherore- sultante actual. NMAKE primero chequea si GLOBALS.H, en el directo- rio actual, ha variado con respecto al fichero resultante C: C600 INCLU- DE GWBALS.H Si ha variado ejecuta la orden COPY. El proceso serepite para el resto de los ficheros. Estos ultimos ejemplos demuestran como NMAKE puede utilizarse para realizar otros trabajos diferentes al de mantenimiento de programas. Las reglas de inferencia son plan tillas desde las cuales NMAKE decideque hacer con un bloque de descripci6n cuando no contiene ficheros depen· dientes u 6rdenes. Una regIa de inferencia se define de la siguiente forma: orden representa las 6rdenes para construir los ficheros con exten· si6n a_ext desde los ficheros con extensi6n desde_ext. caracter afiadido D F B R camino (path) nombre base extensi6n si no no si no si SI SI no si no no f{desde-path J}.desde_extf{ a-path J}.a_ext: orden]
  • 567. .C.OBJ: CL -c $< La macro $< representa el nombre de un fichero dependiente que ha variado con respecto al fichero resultante. Cuando NMAKE encuentra una descripcion de un bloque sin orde- nes busca, apoyandose en Ias extensiones dadas, una regIa de inferencia que especifique como crear el fichero resultante desde Ios ficheros depen- dientes. Si no existe un fichero dependiente, NMAKE busca una regIa de inferencia que especifique como crear Ia depend encia desde otro fichero con el mismo nombre base. # RegIa de inferencia .oBJ.EXE: LINK $<; # Bloque 1 PROGOI.EXE: PROGOI.OBJ # Bloque 2 PROG02.EXE: PROG02.oBJ LINK ICO PROG02, , , MILIB.LIB; En este ejempIo, Ia regIa de inferencia es aplicable a ficheros depen- dientes con extension .oBi y ficheros resultantes con extension .EX£. Como en el bloque 1 no hay ordenes NMAKE aplica Ia regIa de inferencia reem- plazando $< por PROGOI.OBJ. En cambio.' como en el bloque 2 hay or- denes NMAKE no aplica Ia regIa de inferencia. Las reglas de inferencia solamente son titiles cuando existe una corres- pondencia, uno a uno, entre ficheros con extension desde_ext y ficheros con extension a_ext. Las reglas de inferencia eliminan Ia necesidad de po- ner Ias mismas ordenes en varios bloques.
  • 568. Las reglas de inferencia pueden especificarse en un fichero de descrip- ciones, en un fichero a incluir (!INCLUDE) 0 en el fichero TOOLS.INI. NMAKE utiliza las sig~ientes reglas de inferencia predefinidas: .C.OBJ .C.EXE .ASMOBJ $(CC) $(CFLAGS) Ie $*.C $(CC) $(CFLAGS) $*.C $(AS) $(AFLAGS) $*; CL Ie $*.C CL $*.C MASM $*; Las directrices permiten ejecutar 6rdenes condicionalmente, visualizar men- sajes de error, incluir el contenido de otros ficheros y actuar sobre algunas opciones de NMAKE. La sintaxis correspondiente es la siguiente: !IF expr si la expr es verdad, se ejecutan las sentencias entreel siguiente !ELSE 0 !ENDIF !ELSE si la expr es falsa, se ejecutan las sentencias entre !ELSE y!ENDIF !IFDEF macro si la macro esta definida, se ejecutan las sentencias en· tre el siguiente !ELSE 0 !ENDIF. !IFNDEF macro si la macro no esta definida, se ejecutan las sentencias entre el siguiente !ELSE 0 !END IF.
  • 569. CAPITULO 16: LIBRERIAS Y UTILIDADES DEL COMPILADOR 589 !CMDSWITCHES {+ I-lope activa (+) 0 desactiva (-) la opci6n ope especificada. ope puede ser ID, II, IN y IS. Antes del signo + 0 - es necesario un espacio. #prog1602.mak IINCLUDE <reglas.txt> ICMDSWITCHES +D prog1601.exe:prog1601.obj IIFDEF DEBUG I IF H$(DEBUGj"= = HS" LINK ICO prog1601.obj; I ELSE LINK prog1601.obj; I ENDIF IELSE I ERROR maero DEBUG no dejinida. IENDIF Este ejemplo lee y evalua el fichero reglas.txt. Activa la opci6n ID de NMAKE, la cual visualiza las fechas de los ficheros chequeados. Comprueba si la macro DEBUG esta definida y si 10 esta y su valor es "s" invoca al enlazador con la opci6n Ica y si su valor no es "s" invoca al enlazador sin la opci6n ICO. Si no esta definida visualiza un mensaje de error y fina-' liza NMAKE. NMAKE IF prog1602.mak DEBUG=s NMAKE prog1602.mak Si una macro esta definida en mas de un lugar, la ejecuci6n de la misma se hace en el primer lugar que se encuentre al aplicar el siguiente orden de prioridades:
  • 570. 1 linea de 6rdenes 2 fichero de descripciones 0 fichero a incluir 3 variables de entorno 4 fichero TOOLS.INI 5 macros predefinidas Cuando necesitamos emitir una orden con una lista de argumentos que ex- cede de la longitud maxima impuesta par DOS (127 caracteres), la solu-. ci6n es generar un fichero en linea. NMAKE puede generar ficheros en li- nea que serviran como ficheros de respuestas automaticas para otros programas. Para generar un fichero en linea la sintaxis es la siguiente: jich-resultante: jich_dependiente . orden < <Uich_lJ texto del jichero en llnea «[KEEP I NOKEEPj La totalidad del texto especificado entre angulos es colocado en un fichero en linea nombrado jich_l. Si el nombre se omite se crea un fichero temporal; en otro casa, el fichero sera temporal si se finaliza con NOKEEP (opd6n por defecto), 0 permanente si se finaliza con KEEP. En cualquier caso NMAKE s610crea el fichero si la orden es ejecutada. miprog.exe: modJ.ob} mod2.ob} mod3.ob} mod4.ob} mod5.ob} LINK @« modI +mod2 +mod3 +mod4 +mod5, miprog; « Este ejemplo crea un fichero temporal equivalente a un fichero de res- puesta automatica para la orden LINK.
  • 571. Con la orden NMAKE se pueden utilizar los caracteres asterisco (*) e inte- rrogante (?) para especificar ficheros en general. Otros caracteres especia- les son los siguientes: simbolo de escape. Es utilizado para que NMAKE interprete los caracteres especiales que se describen a continuaci6n como caracteres normales. La descripci6n de un fichero puede utilizar una sintaxis especial para de- terminar cada uno de sus componentes. Esta sintaxis es: OJos representa la especificaci6n completa del primer fichero de- pendiente. % IparteF representa una componente. parte es uno de los siguientes ca- racteres: p camino (path) d unidad (drive)
  • 572. OJos % IpF % IdF %IfF % IeF C: C600 SOURCE PROG.C C: C600 SOURCE C: PROG .C Un pseudoobjetivo es un objetivo que no es un fichero en un bloque de descripci6n. Un pseudoobjetivo es un nombre que sirve para construir un grupo de ficheros 0 ejecutar un grupo de 6rdenes. Por ejemplo: AL--.DIA: *.C ICOPY $** A: PROYECTO Cuando NMAKE evalua un pseudoobjetivo siempre considera que10s ficheros dependientes han variado. En el ejempl0 anterior, NMAKE copia todos 10sficheros con extensi6n .C a la unidad y directorio especificados.
  • 573. ID IF prog.mak DEBUG=s El fichero especial denominado TOOLS.INI tiene para NMAKE la misma funci6n que el fichero AUTOEXEC.BAT para DOS. Cada vez que se eje- cuta NMAKE busca en el fichero TOOLS.INI las sentencias asociadas con la etiqueta [NMAKE] para ejecutarlas y a continuaci6n ejecuta el fichero de descripciones especificado. El ejemplo que se presenta a continuaci6n, prog1603.mak, ha side disefia- do para que trabaje con el soporte del fichero TOOLS.INI; es un buen ejem- plo para fijar des de el punta de vista pnictico los conceptos expuestos an- teriormente. La orden de ejecuci6n es de la siguiente forma: El fichero prog1601.ini es el soporte que nos va a dar el fichero TOOLS.INI. Contiene las opciones para el compilador, para el enlazador y para la utilidad LIB, asi como las reglas de inferencia resultantes. De esta forma quedan definidas las opciones y las reglas de inferencia por defecto. Por 10 tanto, en 10 sucesivo, asumiremos que dicho contenido ha sido es- crito en el fichero TOOLS.INI. # prog160l.ini # Estas sentencias jormardn parte del jichero TOOLS.INI
  • 574. !IFDEF DEP CFLAGS=!Od IZi IW2 LFLAGS=ICO IE INOI !ENDIF # Verificar si ha sido dejinida la macro INC para habilitar la # compilacton y el enlace incremental !IFDEF INC CFLAGS=lqc IGi IZr IW2 LFLAGS=IINC INOI !ENDIF !IFDEF DEP ! IFDEF INC ! ERROR depuracton y compilacion incremental simultdneamente ! ENDIF !ENDIF # Redejinir la regia de injerencia CC dejinida en TOOLS.INI .c.obj: !$(CC) $(CFLAGS) $(DEBUG) Ie $< .c.exe: !$(CC) $(CFLAGS) $(DEBUG) $< llink $(LFLAGS) PROYEC16: FUNC16*.C MAINOBJ $(CC) $(CFLAGS) $(DEBUG) Ie FUNC16*.C !LIB PROYEC16 $(LIBFLAGS) -+$(**B); LINK $(LFLAGS) PROYEC16.LIB, $(@B).EXE; $(@B) 1. Si en la orden NMAKE se ha definido la macro NDEBUG se in- habilita la macro "assert", definida en <assert.h >, que es utili- zada can fines de depuraci6n.
  • 575. !IFDEF DEP CFLAGS = IOd IZi IW2 LFLAGS=ICO IE INOI !ENDIF # Verificar si ha sido dejinida la macro INC para habilitar la # compi/acion y el enlace incremental !IFDEF INC CFLAGS = Iqc IGi IZr IW2 LFLAGS=IINC INOI !ENDIF !IFDEF DEP ! IFDEF INC ! ERROR depuracion y compilacion incremental simultcineamente ! ENDIF !ENDIF # Redejinir la regIa de injerencia CC dejinida en TOOLS.INI .c.obj: !$(CC) $(CFLAGS) $(DEBUG) Ie $< .c.exe: !$(CC) $(CFLAGS) $(DEBUG) $ < llink $(LFLAGS) PROYEC16: FUNC16*.C MAIN.OBJ $(CC) $(CFLAGS) $(DEBUG) Ie FUNC16*.C !LIB PROYEC16 $(LIBFLAGS) -+$(**B); LINK $(LFLAGS) PROYEC16.LIB, $(@B).EXE; $(@B) 1. Si en la orden NMAKE se ha definido la macro NDEBUG se in- habilita la macro "assert", definida en <assert.h >, que es utili- zada con fines de depuraci6n.
  • 576. 2. Si en la orden NMAKE se ha definido la macro DEP se estable- cen las opciones de compilaci6n y enlace que permitan depurar el programa. 3. Si en la orden NMAKE se ha definido la macro INC se establecen las opciones que permitan la compilaci6n y el enlace incremental. 4. Verifica que las macros DEP e INC no hayan sido definidas si- multaneamente. Esta acci6n se considera como un error. 5. Si en la orden NMAKE hemos definido alguna de las macros an- teriores, las reglas de inferencia por defecto, seran modific~das con las nuevas opciones; en otro caso las opciones por defecto perma- neceran inalteradas. Por ultimo hemos escrito dos bloques para mantener un supuesto pro- yecto, PROYEC16, compuesto por una funci6n principal MAINC y un con- junto de funciones auxiliares FUNC16*.C. El primer bloque genera un fi- chero PROYEC16.EXE y 10 ejecuta ($(@B)). El segundo bloque compila la funci6n principal MAIN, utilizando para ello las reglas de inferencia. El depurador Code View de Microsoft es un programa interactivo que nos ayudara a localizar errores rapidamente. Code View puede utilizarse sola- mente con ficheros .exe no se puede utilizar con ficheros .com. Cuando depuramos un programa es bueno tener presente la siguiente consideraci6n: el compilador C permite escribir mas de una sentencia en una misma linea. Por ejemplo: Esta forma de escribir un program a dificulta la depuraci6n, ya que no es posible acceder alas sentencias individuales que componen la linea. Por ello, se recomienda escribir las sentencias anteriores de la siguienteforma: car = texto[i]; if (car = = < n') + +lineas;
  • 577. Cuando un programa que se desea depurar se compila se debe especificar la opcion IZi. Esta opcion indica al compilador que incluya numeros de linea e informacion extra para el depurador en el fichero objeto. Si en un programa compuesto por varios modulos, solamente desea- mos depurar algunos de ellos, estos deben ser compilados con la opcion IZi y el resto con la opcion IZd. La opcion IZd afiade al fichero objeto numeros de linea, pero no informacion extra, con 10 que el espacio ocupa- do en el disco y en la memoria es menor. Tambien debe especificarse la opcion IOd la cual impide la optimizacion, ya que esta puede dificultar la depuracion. Este ejemplo compila y enlaza el fichero fuente progOJ.c. EI resultado es un fichero objeto con informacion para el depurador, que es enlazado automaticamente con la opcion ICO (abreviatura de ICODEVIEW). EI siguiente ejemplo compila moduloOJ.c, produciendo un fichero moduloOJ.obj con la informacion total necesaria para el depurador; com- pila modulo02.c produciendo un fichero objeto con informacion limitada. A continuacion se utiliza CL otra vez para enlazar los ficheros objeto re- sultantes. Esta vez, CL no compila de nuevo, ya que los argumentos no tienen extension. La opcion IZi en esta ultima orden hace que el enlazador (linker) sea invocado con la opcion ICO. El resultado es un fichero ejecu- table, moduloOJ.exe, que permite depurar moduloOJ.c. CL /Zi IOd Ie moduloOJ.c CL IZd IOd Ie modulo02.c CL IZi moduloOI modulo02 Una vez compilado un programa con las opciones necesarias para depu- rarIo invocaremos a Code View. La sintaxis es la siguiente:
  • 578. fichero-ejecutable es el nombre del fichero ejecutable que se quiere depmar. son los parametros que opcionalmente podemos pa- sar en la linea de 6rdenes al fichero ejecutable. Code View utiliza la memoria expandida si EMM esta instalado 0 la memoria extendida si XMM esta instalado. Inn inicializa el modo de 25 lineas (nn=25), de 43 lineas (nn=43) o de 50 lineas (nn = 50). /C6rdenes permite especificar una 0 mas 6rdenes que se ejecutanin in- mediatamente despues de invocar a CV. ID[Kb] ejecuta CV empleando la tecnica de overlays con el fin de dis- poner de mas espacio para el programa. Kb es un valor (mul- tiplo de 16)entre 16y 128que especifica el tamafio del overlay.
  • 579. no permite el intercambio de paginas de video entre CV y el programa. Opcion por defecto. permite utilizar el controlador de interrupciones. Esta opcion permite la utilizacion de Ctrl +C y de Ctrl + Break en ordena- dores no compatibles con IBM. inhabilita el raton. Utilizar esta opcion cuando se depure una aplicacion que soporta raton. permite utilizar los 4 registros especiales del 80386. Esto da lugar a una mayor velocidad. Esta opcion no esta soportada en modo protegido. permite el intercambio de paginas de video entre CV y el programa. Cuando invocamos al depurador Code View (CV nombre_programa) se visualiza una pantalla dividida en tres areas. El area primera ocupa la linea 1, la cual visualiza el menu principal. El area segunda ocupa las lineas 2 a 24 (dependiendo del tipo de tarje- ta de video podemos disponer de mas lineas) y es utilizada para visualizar las ventanas del depurador. Para movernos dentro de estas ventanas pode- mos utilizar las teclas de movimiento del cursor y las teclas PgUp y PgDn. Para pasar de una ventana a otra se puis a la tecla F6 0 Shift + F6. El area tercera ocupa la linea 25 y es utilizada para indicar las accio- nes que podemos to mar.
  • 580. [BP+flflfI'Il [BP+flflfl81 [BP+flflflCl II If1: 11: 12: 13: 1'1: IS: float float float local x = 1. fIfIfIfIfI y = 2.fIfIfIfIfI z = 3.fIfIfIfIfI irl"••• ···,·;S"' ..·.:i.'WIl reg = flfIfI& = If11A = flfIBl = '1f1f1f1 = If11& = If12f1 SI = fI'I82 01 = fI'I82 OS = 3f1C& ES = 2BsO SS = 3f1C& CS = 2B7D IP = fllZE fL = fl2f12 else Md z: else if ( y > Z ) Ma y: else Moryl byte OS:fIfIfIfI(ACTIVE) CO 2f1 C& '1f1fIfI9A ffl fE 10 ffl 8B fl2 fIfI2A 51 fl3 fIfI2A BB flA fIfI2A '1C 2B fll fll fll fIfIfl2 fl3 ff ff ff ff ff ff ff ff ff ff ff ff ff ff SO 2B A2 2& t-------------<:coMMand Las operaciones minimas que debe incluir un depurador son las si- guientes: Permite ver la sentencia del programa que es ejecutada. Code View incluye las siguientes opciones: Ejecutar una sentencia cada vez, incluidas funciones definidas por el usuario. Esta modalidad se activa y se continua pulsando la tecla F8. Si no queremos que las funciones se ejecuten senten- cia a sentencia pero si la funci6n principal main( ) utilizamos la tecla FIO. Un punta de parada es una pausa que se hace en un lugar determi· nado dentro del programa. Esto permite comprobar los valores de las variables en ese instante. Colocar los puntos de parada donde se sospeche que esta el error.
  • 581. Para poner 0 quitar una pausa se coloca el cursor en ellugar donde va a tener lugar la pausa y se pulsa F9. Se pueden poner tantos pun- tos de parada como necesitemos. Las ventanas local y watch permiten observar los valores de las va- riables y de las expresiones especificadas. Al ejecutar el programa paso a paso podemos observar en estas ventanas los valores que van tomando las variables. Si pulsamos la tecla F5 la ejecuci6n continua hasta el final del pro- grama 0 hasta el primer punta de parada, si existe. Cuando se pulsa F7 el programa se ejecuta desde la ultima senten- cia ejecutada hasta la linea donde se encuentra el cursor. Si se desea procesar el programa paso a paso de una forma automa- tica, ejecutar la orden Animate del menu presentado por la opci6n Run. Una vez iniciada la animaci6n de un programa puede ser de- tenida pulsando cualquier tecla. La velocidad de ejecuci6n automa- tica se puede aumentar 0 disminuir mediante la orden Trace Speed del menu Options. Ejecutar la orden Restart cada vez que iniciemos de nuevo el proceso de depuraci6n. Para salir del depurador ejecutar la orden Exit del menu presentado por la opci6n File. Cuando se entra en Code View, mediante la orden CV, 10 primero que apa- rece es el menu principal y tres ventanas: la ventana local para mostrar las variables locales, la ventana source para mostrar el texto del programa a
  • 582. depurar y la ventana command para emitir 6rdenes para el depurador. El menu principal consta de ocho opciones: File, Edit, View, Search, Run, Watch, Options, y Calls. Para seleccionar una de las opciones presentadas, se puede optar por cualquiera de las dos formas siguientes: • Pulsar la tecla AIt, para activar el menu principal, y despues la te- cla correspondiente a la letra que se presenta en alto brillo 0 color diferente (es indiferente el utilizar mayusculas 0 minusculas). Por ejemplo W para activar el menu correspondiente a la opci6n Watch. • Pulsar la tecla Ait para activar el menu principal y utilizando las teclas de movimiento del cursor elegir la opci6n deseada (opci6n que se presentani en video invertido respecto al existente 0 en color di- ferente) y pulsar la tecla Enter. Para seleccionar una orden correspondiente al menu presentado por una opci6n del menu principal se puede pro ceder de cualquiera de las dos formas siguientes: • Pulsar la tecla correspondiente a la letra que se presenta en alto bri- 110 0 color diferente (es indiferente el utilizar mayusculas 0 minus- culas). Por ejemplo si se esta en el menu presentado por la opci6n File y se quiere salir de Code View, se pulsa la tecla x. • Moverse a la orden deseada por medio de las teclas de movimiento del cursor y pulsar Enter. Algunas 6rdenes de estos menus tienen escrito a su derecha el nombre de una tecla 0 combinaci6n de teclas que realizan la misma operaci6n. Par ejemplo la primera orden del menu de la opci6n Watch es "Add Watch... Ctrl +W". Esto significa que al pulsar las teclas Ctrl +W se ejecuta la or· den Add Watch. Algunas 6rdenes abren una ventana de dialogo; por ejemplo la orden "Find ..." del menu presentado por la opci6n Search. En este caso respon- deremos alas cuestiones planteadas y finalizaremos pulsando a continua- ci6n Enter « OK ».
  • 583. Para obtener ayuda sobre cualquier orden, seleccionarla y pulsar Fl. Para abandonar la pantalla de ayuda pulsar la tecla Ese. Code View esta disefiado para utilizar un raton de Microsoft 0 uno com- patible con este. 1. Se apunta a la opcion deseada del menu y se puisa el boton iz- quierdo del raton. Para enrollar/desenrollar el texto sobre la ventana activa se dispone de una barra de scroll vertical y otra de scroll horizontal. Ambas tienen en sus extremos unas flechas que indican el desplazamiento imaginario de la pantalla sobre el texto cuando se efectua la accion de scroll y un cursor que se desplaza a 10 largo de la linea de scroll, que indica la posicion relati- va del cursor de pantalla con respecto a los limites del texto. 1. Para avanzar 0 retroceder una linea se apunta a la flecha superior o inferior de la barra de scroll vertical y se pulsa el boton izquier- do del raton. Para desplazar el texto una posicion a la izquierda o a la derecha se apunta a la flecha derecha 0 izquierda de la ba- rra de scroll horizontal y se pulsa el boton izquierdo del raton. 2. Para avanzar 0 retroceder una pagina, se coloca el cursor del ra- ton sobre la linea de scroll vertical, entre el cursor de scroll y la flecha inferior 0 entre el cursor de scroll y la flecha superior y se pulsa el boton izquierdo del raton. La operacion es analoga para desplazar el texto una pagina hacia la izquierda 0 hacia la dere- cha; eso S1, actuando sobre la barra de scroll horizontal. 3. Si se apunta al cursor de scroll vertical de la ventana de texto y se tira, con el boton izquierdo del raton puisado, hacia arriba 0 hacia abajo arrastrandolo sobre la linea de scroll, el texto se des- plaza hacia abajo 0 hacia arriba. La accion se ve cuando se deja
  • 584. de pulsar e! boton. Esta misma operacion se puede realizar sabre la linea de scroll horizontal para desplazar el texto hacia la izquierda o hacia la derecha. Es posible variar el tamafio de una ventana. Para ello, se apunta a la linea de separacion entre ventanas y con el boton izquierdo del raton pul- sado se tira en la direccion apropiada para agrandar 0 reducir la ventana. Para activar una ventana, apuntar a cualquier lugar dentro de la mis- ma y pulsar el boton izquierdo del raton. Para activar 0 desactivar cualquier accion dentro de una ventana de dhilogo, apuntar al espacio entre corchetes 0 entre angulos y pulsar el ba- ton izquierdo del raton. Muchas de las ordenes que se exponen a continuacion requieren la selec- cion previa de un conjunto de caracteres, palabras 0 lineas. Para realizar esta operacion dentro de la ventana activa, colocar el cursor al principio del texto a seleccionar y manteniendo pulsada la tecla Shift, marcar eltex· to desplazando e1cursor con las teclas de movimiento. Tambien es posible seleccionar texto con el raton. Para ello, se apunta al principio del texto a seleccionar y manteniendo pulsado el boton izquierdo se tira en la direccion deseada hasta marcar el texto. Cuando se ejecuta esta orden se nos pregunta por el nombre del nuevofi- chero que deseamos cargar en la ventana source. Este fichero puede serun fichero fuente, un fichero .h 0 cualquier otro fichero de texto. Cuandose acabe de ver e1fichero cargado se puede volver a cargar el fichero original.
  • 585. Permite se1eccionar uno de 10sm6du10s fuente que componen e1programa y cargarlo en 1a ventana source. Permite escribir todo 0 parte del fichero visualizado en 1aventana activa a un fichero 0 dispositivo especificado (por ejemp10 LPTl). Esta orden permite salir tempora1mente a1DOS pudiendo as! ejecutar cual- quier otra tarea bajo e1sistema operativo. La vuelta a Code View se hace ejecutando 1a orden Exit. Deshace 10s cambios efectuados en 1a linea que estamos editando. No es posib1e vo1ver a una linea ya abandonada y restaurarla. Copia e1texto se1eccionado de 1aventana activa y 10salva en una memoria intermedia, permaneciendo en 1a misma hasta que copiemos otro texto.
  • 586. Copia en la ventana activa el texto almacenado en la memoria intermedia. Podemos realizar las dos operaciones siguientes: Reemplazar texto: seleccionar el texto que deseamos reemplazar en la ventana activa y ejecutar Paste. Insertar texto: mover el cursor a la posici6n deseada dentro de la ven- tana activa y ejecutar Paste. El texto se inserta a continuaci6n del cursor. • c6digo fuente solamente • instrucciones en lenguaje ensamblador • c6digo fuente mezclado con las correspondientes instrucciones en lenguaje ensamblador. Abre una nueva ventana source. Una ventana fuente sirve para mostrar el c6digo fuente de un programa. El c6digo puede aparecer en uno de los tres modos siguientes: Abre una nueva ventana memory y visualiza en ella una zona contigua de memoria. Para especificar la direcci6n de comienzo, ejecutar la orden "Me- mory Window" del menu Options.
  • 587. Activa/desactiva la ventana register. Esta ventana visualiza el estado ac- tual de los registros del microprocesador 80x86. Activa/desactiva la ventana 8087. Esta ventana visualiza el estado actual de los registros del coprocesador matematico 80x87. Activa/desactiva la ventana local. Esta ventana visualiza todas las varia- bles locales de la funcion que actualmente se este ejecutando. En esta ventana no se pueden afiadir ni borrar variables. Si se permite modificar el valor de una variable. Activa/desactiva la ventana watch. Esta venta:na muestra los valores de las variables y expresiones seleccionadas. Para afiadir una variable 0 una expresion a la ventana watch, ejecutar la orden "Add Watch" del menu Watch. Es posible modificar el valor de una variable 0 de una expresion incluida en cualquiera de estas ventanas. Esto se hace escribiendo encima del valor actual. Para restaurar el valor original pulsar la tecla Esc. Cualquier elemento de datos (estructura, array 0 puntero) puede ser expandido 0 contraido si la ventana, local 0 watch, esta activa. Un elemen- to de datos que esta precedido por el signo mas (+) significa que puede
  • 588. ser expandido y si esta precedido por el signo menos (-) significa que pue- de ser contraido. Para expandir 0 contraer un elemento de datos, apuntar al mismo can el raton y pulsar dos veces el boton izquierdo; 0 bien, mover el cursor al elemento de datos y pulsar Enter. Activa/desactiva la ventana command. Esta ventana permite emitir orde- nes para Code View. Estas ordenes nos permiten hacer de una forma direc- ta las operaciones que realizamos a traves de los menus. Activa/desactiva la ventana help. Esta ventana visualiza informacion de ayu- da a cerca de Code View y palabras clave del lenguaje. • ejecutar help • pulsar Shift + Fl • situar el cursor sobre una palabra clave dellenguaje, sobre una Of- den de un menu 0 sobre una accion en una ventana de dialogo y pulsar Fl. Esta orden hace que se visualice la pantalla de los resultados. Para volver a la ventana de texto pulsar F4. Esta orden permite agrandar la ventana activa si esta a tamafio normal 0 ponerla a tamafio normal si esta agrandada. Cuando la ventana esta agran- dada aparece sobre el menu View la orden Restore en lugar de Maximize.
  • 589. Para realizar esta operaci6n con el rat6n, apuntar a la cajita de la es- quin.asuperior derecha de la ventana y pulsar el bot6n izquierdo del rat6n. Permite modificar el tamafio de la ventana activa utilizando las teclas de movimiento del cursor. Para finalizar esta operaci6n pulsar Enter; para aban- donar esta operaci6~ sin modificaciones, pulsar Ese. Esta orden cierra la ventana activa. Para realizar esta operaci6n con el ra- t6n, apuntar a la cajita de la esquina superior izquierda de la ventana y pulsar el bot6n izquierdo del rat6n. Utilizar esta orden para buscar en la ventana activa la siguiente ocurrencia del texto especificado. Podemos requerir buscar palabras enteras (Whole Word), hacer distinci6n 0 no entre letras mayusculas y minusculas (Case Insensitive) y utilizar expresiones regulares (Regular Expression). Cuando se requiere utilizar expresiones regulares pueden utilizarse como como dines los siguientes caracteres: la localizaci6n del texto escrito despues de este canicter debe darse al principio de una linea. la localizaci6n del texto escrito antes de este canicter debe darse al final de una linea.
  • 590. [ ] localizar uno de los caracteres del conjunto especificado entre cor- chetes. Dentro de los corchetes pueden utilizarse los caracteres es- peciales: para indicar los limites del conjunto de caracteres que seae· sea especificar. cualquier canicter de los indicados precedido por este caracter pierde su significado especial y es considerado como un caracter normal. Permite buscar un texto seleccionado previamente. El texto seleccionado debe estar sobre una unica linea.
  • 591. Esta orden permite repetir la ultima busqueda realizada por Find 0 por Select Text. Esta orden se utiliza para buscar una etiqueta 0 un nombre de una funci6n en un programa en lenguaje ensamblador. Esta orden hace que la siguiente sentencia a ejecutar sea de nuevo la pri- mera sentencia de la funci6n main( ). lnicializa todas las variables que in- tervienen en el programa. Ejecutar esta orden antes de una nueva ejecuci6n. Ejecuta el programa paso a paso automaticamente desde la instrucci6n ac- tual. Si el programa ya ha side ejecutado anteriormente hay que ejecutar previamente la orden Restart. Una vez iniciada la animaci6n de un progra- ma puede ser detenida pulsando cualquier tecla. La velocidad de ejecuci6n automatica se puede aumentar 0 disminuir mediante la orden Trace Speed del menu Options. Esta orden permite especificar los argumentos en linea de 6rdenes necesa- rias para la ejecuci6n del programa.
  • 592. Esta orden permite registrar la historia del proceso de depuraci6n 0 utiIi- zar la historia ya registrada. Durante esta operaci6n se crean dos ficheros con un nombre base (prog) igual al nombre del programa. Estos ficherosson: Para registrar la historia del proceso de depuraci6n, activar HistoQ On y ejecutar el programa paso a paso. Para retroceder y avanzar sobre la historia creada hasta el momenta actual, utilizar las teclas Shift +F8 Y Shift +FIO respectivamente. Esta orden repite toda la historia de un proceso de depuraci6n si la orden History On esta activa. Para repetir parte de la historia, desde el principio hasta un determi- nado punto, primero situarse en el punta de la historia hasta el cual sequiere repetir el proceso de depuraci6n y a continuaci6n ejecutar la orden Replay.
  • 593. Esta orden permite afiadir en la ventana watch variables y expresiones cuyo valor se quiere visualizar. Los valores senin visualizados cuando se ejecute el programa paso a paso. Una expresi6n puede ser una variable de cualquier tipo 0 una expre- si6n C valida. Una expresi6n de relaci6n dara un resultado falso (0) 0 ver- dadero(l). Para visualizar una expresi6n de estas con formato de salida diferente al indicado en el programa, escribir una coma seguida por un canicter de formato. resto resto, f resto, x (formato: el indicado en el programa) (formato: real) (formato: hexadecimal) Esta orden permite borrar una expresi6n 0 todas las expresiones de la ven- tana Watch. Para borrar una expresi6n, seleccionarla y pulsar Enter. Para borrar todas la expresiones elegir la opci6n Clear All. Esta orden permite poner un punta de parada. Un punta de parada es una pausa que se hace en un lugar determinado del programa. Esto nos permi- te verificar los valores que tienen las variables en ese instante. Colocar los puntos de parada donde se sospeche que puede darse un error.
  • 594. Para poner un punta de parada, colocar el cursor en el lugar donde debe tener lugar la acci6n y pulsar F9 0 ejecutar la orden Set Breakpoint. Para quitar un punto de parada, situar el cursor en ellugar donde esta puesto y pulsar F9. Define el formato con el que se visualizara una zona de memoria en la ven- tana memory. Permite seleccionar la velocidad de ejecuci6n paso a paso para la orden Animate.
  • 595. Esta orden permite seleccionar e1tipo de expresiones que se van a evaluar, esto es, expresiones Basic, C 0 Fortran. Si activamos la opci6n Auto, el de- purador reconoceni automaticamente en funci6n del programa cargado el tipo de expresi6n que tiene que evaluar. Activa/desactiva e1intercambio de paginas de video entre Code View y el programa que se esta depurando (ver las opciones IF y IS). La opci6n swap desactivada requiere menos memoria y hace que el proceso sea mas rapido pero tiene una serie de limitaciones que no se dan cuando esta activa. Indica a Code View si en la repetici6n de la historia del proceso de depura- ci6n se utiliza el fichero .cvh, el fichero .cvi 0 ambos (ver la orden History On descrita anteriormente). Esta orden solamente puede ser ejecutada si se tiene un procesador 386. Cuando la opci6n 386 esta activada la ventana register visualiza los regis- tros en el formato 80386. Tambien permite ejecutar instrucciones que refe- rencian registros de 32 bits. Si no esta activa cualquier dato almacenado en la parte alta de un registro de 32 bits se perdera. Esta opci6n no esta disponible en modo protegido (CVP).
  • 596. Esta orden presenta una lista de nombres de funciones, ademas del modu- lo principal, que han sido llamadas para su ejecucion y que aun no han finalizado. Cada llamada es mostrada con los argumentos que se han pa- sado a la funcion. A esta lista de llamadas en ocasiones se Ie da el nombre de pila porque el nombre que esta en la parte superior es la ultima llamada. Cada vezque una funcion termina de ejecutarse su nombre desaparece de la pila. Por ejemplo si el modulo principal Ml llama a una funcion Sl, esta a una funcion S2 y esta a una funcion S3, cuando la funcion S3 este en ejecucion, en la pila habra, de arriba a abajo: S3, S2, Sl, Ml. Cuando fi- nalice la ejecucion de S3, en la pila quedara S2, Sl, Ml y as! sucesivamente. Supongamos que detenemos la ejecucion cuando se esta ejecutando la funcion S3 y visualizamos el menu Calls. Si mediante las teclas de movi- miento del cursor nos situamos sobre el nombre Sl y pulsamos Enter, Code View coloca el cursor sobre la siguiente sentencia que seria ejecutada cuando el control sea devuelto a la funcion Sl y muestra en la ventana local las variables correspondientes a esta funcion. Si en este instante pulsamos la tecla F7la ejecucion continuara hasta esta sentencia (sentencia sobre la que esta el cursor). Comprime un fichero ejecutable reduciendo el espacio ocupado por la in- formacion afiadida al fichero para hacer posible su depuracion. EI fichero comprimido puede ser cargado por Code View mas rapidamente.
  • 597. Esta utili dad permite crear y modificar una base de datos de ayuda a par- tir de uno 0 mas ficheros que contienen informacion formateada para el sistema de ayuda. Convierte programas en modo protegido para que puedan correr tanto en modo protegido como en modo real. Una vez convertido, el mismo pro- grama puede correr bajo DOS y OS/2. Los programas en modo protegido utilizan librerias enlazadas dina- micamente (DLL) para cargar en tiempo de ejecuci6n las funciones desde las librerias almacenadas en ficheros. Los programas en modo real inclu- yen las funciones de libreria en el fichero ejecutable. Visualiza y modifica el contenido de la cabecera de un fichero ejecutable. EXEHDR genera un listado que muestra el contenido de la cabecera del fichero e informaci6n a cerca de cada segmento en el fichero.
  • 598. Borra todos los ficheros del subdirectorio oculto DELETED del directorio actual 0 deldirectorio especificado. Mueve el fichero 0 ficheros especificados al subdirectorio oculto DELE- TED. Para recuperar estos ficheros utilizaremos UNDEL. Mueve el fichero 0 ficheros especificados del subdirectorio oculto DELE- TED, al directorio padre.
  • 599. La potencia de Microsoft C aumenta con la facilidad que tiene para incor- porar 0 Hamar a rutinas en lenguaje ensamblador. En este capitulo se ex- plica c6mo insertar en un m6dulo C rutinas escritas en lenguaje ensambla- dor (rutinas en linea en ensamblador) y c6mo escribir un m6dulo separado en lenguaje ensamblador para despues enlazarlo con otros m6dulos C. Toda persona conocedora dellenguaje ensamblador es consciente de la eficiencia y velocidad de las rutinas desarroHadas en dicho lenguaje. Tam- bien sabe que el programar rutinas largas en lenguaje ensamblador puede ser tedioso. Por esta raz6n, ellenguaje ensamblador es a menudo reserva- do para pequefias tareas 0 rutinas espedficas dentro de un programa en lenguaje de alto nive!. • Hamar alas rutinas del DOS y del BIOS con la instrucci6n INT • crear rutinas residentes (TSR)
  • 600. Las rutinas en lenguaje ensamblador incorporadas en un programaC pueden hacer que el programa no sea portable, por 10 que se aconseja que estas rutin as se escriban como funciones 0 como m6dulos separadas para que, en caso necesario, puedan ser reescritas con facilidad. RUTINAS EN LENGUAJE ENSAMBLADOR EN LINEA CON SENTENCIAS C Una rutina en ensamblador, escrita en linea con las sentencias C que for- man el programa, va precedida por la palabra clave_asm. La palabra_asm llama al ensamblador; puede aparecer en cualquier lugar valida para una sentencia C e ira seguida por una instrucci6n en ensamblador 0 par un grupo de instrucciones en ensamblador encerradas entre llaves. main( ) [ void pagina(short),o pagina(O),o } void pagina(short pag) [ mov ah,5 mov ai, byte ptr pag int lOh,o /lamar al BIOS Este ejemplo muestra una funci6n C formada por una rutina en en- samblador. Las instrucciones en ensamblador van encerradas entre Haves y encabezadas por la palabra reservada _asm.
  • 601. Una rutina en ensamblador puede tambien especificarse no como una funcion, sino como un bloque incluido en el propio modulo C. # include <stdio.h > main( ) [ system (Hcls"),o printj(HPulse una tecla para continuar "),o _asm [ push mov int pop l ax ah, 0 16h ax ; salvar ax ; funci6n: leer tecla ; llamar al BIOS ; restaurar ax printj(H nFin del proceso"),o l Una rutina en ensamblador puede escribirse tambien de las siguientes formas: 1. Lineas independientes, precedidas cada una de ellas por la pala; bra clave _asm. Por ejemplo: # include < stdio.h > # include <stdlib.h> main( ) [ system (Hcls"),o printj(HPulse una tecla para continuar "),o _asmpush _asm mov _asm int _asm pop ax ah, 0 16h ax ; salvar ax ; funci6n: leer tecla ; llamar al BIOS ; restaurar ax
  • 602. printj(H nFin del proceso"); } 2. Una instrucci6n a continuaci6n de otra, actuando como separa- dor la palabra clave _asm. Por ejemplo: • Sin Havesel compilador no puede distinguir donde finaliza elcadi· go ensamblador y donde comienza el c6digo C. # include <stdio.h > # include <stdlib.h> main( ) [ system t'cls' '); printf(HPulse una tecla para continuizr "); printj(H nFin del proceso"); } En cualquier caso se recomienda incluir el c6digo en lenguaje ensam- blador entre Haves, por las siguientes razones: • Las Havesseparan de una forma clara el c6digo en ensamblador del c6digo C. Las Havesutilizadas en un bloque _asm no afectan al ambito deutili· zaci6n de las variables como sucede en C. En un programa Microsoft C, un bloque _asm soporta el conjunto com· pleto de instrucciones correspondientes a los procesadores 80286y 80287 pero no reconoce las instrucciones que sean especificas de los procesado- res 80386 y 80387. Para utilizar instrucciones especificas de los procesado- res 80286 y 80287 el programa C debe ser compilado con la opci6n /G2.
  • 603. Una constante entera puede ser expresada en las bases 2, 8, 10 y 16 afia- dieildo el sufijo: B, Q 0 0, D y H respectivamente. Tambien puede emplearse la notacion C; por ejemplo las constantes lOOh y OxlOO tienen el mismo significado. Aunque un bloque _asm puede referenciar objetos y tipos de datos C, no puede definir objetos para contener datos utilizando las directrices y ope- radores del MASM. Especificamente, no se pueden utilizar las directrices: DB, DW, DD, DQ, DT y DF 0 los operadores DUP 0 THIS. Tampoco es- tan disponibles las estructuras y registros del MASM; por 10 que no se pueden utilizar las directrices: STRUC 0 RECORD. Sf son soportadas las directri- ces EVEN y ALING. Un bloque _asm en Microsoft C reconoce todas las expresiones soporta- das por el Macro Ensamblador de Microsoft MASM, esto es, cualquier com- binacion de operandos y operadores que den como resultado un unico va- lor 0 una direccion. Tambien reconoce todos los operadores con las siguientes excepciones 0 diferencias: Los segmentos no pueden ser nombrados. Utilizar explicitamente un regis- tro; por ejemplo ES:[BX]. Si se utiliza el operador PTR, debe aparecer an- tes de modificar el segmento. Sise utiliza el mismo identificador para un miembro en mas de una estruc- tura 0 union, el nombre de la estructura 0 union deben especificarse inme- diatamente antes del operador " . ".
  • 604. No es soportado en union con el operador DUP pero puede ser utilizado para devolver e1numero de elementos de un array. Para una variable esca- lar (no array) devuelve el valor 1. No es soportado en union con el operador DUP pero puede ser utilizado para devolver e1tamafio de una variable. El tamafio de una variable es el producto de los valores devueltos por los operadores LENGHT y TYPE. Este operador puede ser utilizado en un bloque _asm para devolver el ta- mafio de un tipo 0 de una variable C. Si la variable es un array, TYPE de- vuelve el tamafio de un elemento del array. Los resultados al aplicar los operadores anteriores y las expresiones equi- valentes en C son los siguientes:
  • 605. El ensamblador utilizado para codificar rutinas en linea no es un macro ensamblador (MASM). Par ella, las macro directrices MACRO, REPT, IRC, IRP y ENDM y los macro operadores < >, !, &, 0,10, WIDTH, MASK y .TYPE no son soportados. En cambio, en un bloque _asm, si es posible incluir directrices del preprocesador de C. Las instrucciones en un bloque _asm pueden utilizar comentarios estilo ensamblador. Par ejemplo: Evitar utilizar este tipo de comentarios en una macro C; ya que C ex- pande la macro en una unica linea 16gica. La pseudoinstrucci6n _emit es similar ala directriz DB de MASM. Per- mite definir un byte en la posici6n actual del segmento de c6digo actual. Este byte se convierte en el byte apuntado por el registro IP (puntero de instrucciones); 10 que equivale a decir que este es el primer byte de la si- guiente instrucci6n a ejecutar. Esto es, _emit provee un mecanismo para introducir instrucciones maquina byte a byte. Una aplicaci6n can creta de _emit es definir instrucciones especificas del procesador 80386 que no es- tan soportadas por un bloque _asm. El siguiente ejemplo define la instrucci6n CWDE del 80386, la cual convierte un valor can signo de 16 bits en AX, a un valor can signa de 32 bits en EAX.
  • 606. 1* Se asume el modo 16 bits */ #define cwde _asm _emit Ox66_asm _emit Ox98 El c6digo maquina equivalente a CWDE es 98 66 (hexadecimal); estos bytes son introducidos por la macro cwde (primero el de menor peso) en el segmento de c6digo como si de la propia instrucci6n se tratara. Por 10 tanto el procesador interpretara la macro cwde como la instrucci6n CWDE del 80386. Como hemos visto, un bloque _asm puede insertarse en un bloque C. Lo- gicamente, las instrucciones del bloque _asm, igual que las sentencias de C, pueden referirse a las variables C y utilizar otros elementos de C. Tales elementos son los siguientes: • Constantes, incluyendo constantes simb6licas y miembros de una enumeraci6n (enum) • Nombres de tipos declarados por typedeJ, generalmente utilizados con operadores tales como PTR y TYPE 0 para especificar los miem- bros de una estructura 0 uni6n.
  • 607. Entendiendo por simbolo C un nombre de v<;lriable,etiqueta 0 un nom- bre de una funci6n (no constantes simb61icas 0 miembros enum) las siguien- tes reglas son aplicables': 1. Solamente un simbolo C puede ser referenciado par una ins truc- ci6n en lenguaje ensamblador. Dos 0 mas simbolos no pueden apa- recer en la misma instrucci6n en ensamblador a menos que se uti- licen con los operadores TYPE, LENGTH 0 SIZE. 2. Las funciones referenciadas en un bloque _asm, deben ser decla- radas anteriormente (funci6n prototipo), con el fin de que en un bloque _asm, Microsoft C pueda diferenciar un nombre de fun- ci6n de una etiqueta. 3. Un bloque _asm no puede utilizar simbolos C que coincidan con palabras reservadas MASM. Por ejemplo, POP, PUSH y SI son palabras reservadas para MASM. 4. Un bloque _asm puede referenciar todas las variables y demas simbolos que esten dentro del ambito del bloque donde aparece _asm. # include <:stdio.h > # include <std!ib.h> main( ) ( void cursor-----xy(unsigned char y, unsigned char x); unsigned char fila, co!; system ("cls "); -Fila= 23· co! = 5·J' , , cursor-----xy(fila,co!); printj("Pu!se una tecla para continuar"); l
  • 608. / * Posicionar el cursor en la pantalla (suponemos pagina 0). * Si fila 0 columna estan fuera de limites, se ignora. */ void cursor-----xy(unsigned char y, unsigned char x) [ _asm [ ; Aceptar valores x, y mov cmp jl cmp jg dl, x dl, 0 fin dl, 79 fin ; dl = columna ; si col < 0, ignorar mov dh, y cmp dh,O jl fin cmp dh,24 jg fin ; Posicionar el cursor mov mov int fin: nop } } ; dh = fila ; si fila < 0, ignorar bh, 0 ah,2 10h ; pagina 0 ; funcion posicionar cursor ; llamar al BIOS Los operadores identificados de la misma forma en C que en MASM (*, I],...)tienen el significado propio del contexto en el que se utilicen. int a[20],o _asm mov a[5], ax a[5] = b,o ; almacena ax en la posicion a+5 / * almacena b en la posicion a+ 10 */
  • 609. Una macro en C debe seI:escrita sobre una unica linea 16gica.Por ello, cuan- do la macro ocupe mas de una linea fisica, utilizaremos como canicter de continuaci6n la barra invertida ( ). Para escribir una macro y evitar re- sultados inesperados, es aconsejable seguir las siguientes reglas: • Comenzar cada instrucci6n en ensamblador con la palabra clave _asm. • Introducir los comentarios utilizando la notaci6n / * */ para que quede perfectamente delimitado. # define pagina(pag) _asm / * Establecer la pdgina de visualizaci6n activa */ [ _asm mov _asm mov _asm int ah,5 ai, byte ptr pag 10h main( ) [ int a[20]; pagina(1); a[5] = 10; printf(H%d n': a[5J); } En este ejemplo las Havespara delimitar el bloque _asm que forma la macro son esenciales. De otro modo el compilador asume las instruccio- nes sobre la misma linea, ala derecha de la macro, como c6digo ensambla- dor adicional.
  • 610. En general, cuando un bloque _asm comienza a ejecutarse no asumimos que un registro tenga un determinado valor; es decir, actuamos de igual forma que con cualquier otra sentencia C. Cuando escribimos un bloque _asm en linea con sentencias C, las instrucciones que componen dicho bloque son libres de alterar los regis- tros AX, BX, CX, y DX. Esto es aplicable tambien a los registros DI y Sl con algunas excepciones (por ejemplo, C utiliza estos registros cuando de- finimos variables tipo register).Sin embargo, si hay que salvar los registros SP y BP, a no ser que tengamos alguna raz6n para cambiarlos de valor. Cuando escribimos un bloque _asm como una funci6n no es necesa- rio salvar los registros AX, BX, CX, DX, ES y flags. Sin embargo, si hay que salvar cualquier otro registro (DI, SI, DS, SS, SP y BP), a no ser que tengamos alguna raz6n para cambiarlos de valor. Las funciones utilizan los registros AX y DX para devolver los resul- tados. Si el valor devuelto es de tipo short, C asume el registro AX y si el valor es de tipo long, C asume los registros DX y AX, colocando en este caso la palabra de mayor peso, del resultado, en el registro DX y la palabra de menor peso, en el registro AX. Para devolver un valor real la funci6n debe colocar el valor en memoria y devolver un puntero a dicho valor (en AX si es near y en DX:AX si es far). Para funciones con bloques _asm, no utilizar eI convenio -Jastcall. Cuando utilizamos el convenio -Jastcall de Hamada a funciones (opci6n /Gr), e1compilador C almacena los argumentos pasados en registros en lugar de en la pila. Esto puede traer problemas ya que no sabemos que re- gistros se han utilizado. Por ejemplo, si la funci6n recibe un panimetro en BX e inmediatamente nosotros almacenamos un valor en BX, el panime- tro se ha perdido. Calcular la raiz cuadrada entera de un numero entero, mediante el si- guiente algoritmo: "la raiz cuadrada entera de un numero entero n es igual al numero de enteros impares (1, 3, ...) que pueden restarse sucesivamente de dicho numero".
  • 611. Comprobar que el numero de enteros impares buscado se correspon- de con el primer numero impar ni no restado, dividido por 2 (division entera). Para ajustar la raiz al entero mas proximo, procederemos de la siguiente forma: raiz = nU2 si raiz*(raiz+])+] < = n entonces raiz 12 - 1 11 - 3 8 - 5 3 - 7 13 - 1 12 - 3 9 - 5 4 - 7 ni = 7, raiz = 7/2 3*4+1 > 12, raiz ni = 7, raiz = 7/2 = 3 3*4+1 < = 13, raiz = 4 main( ) ( short sqrte(short n); / *prototipo */ short numero; printft';, mimero ? "); scanft'%d': &numero); printf(C<%d': sqrte(numero)); }
  • 612. / * Funci6n para ea/eu/ar /a ra[z euadrada de un nro. entero. */ short sqrte(short n) { _asm { ; Entrada: ex ; Salida: ax mov mov mov jmp ex, n bx, ex ax, 1 SHORT eero ; eargar e/ argumento ; haeer una eopia ; primer numero impar ;;.n<=O? /azol: sub ex, ax ; ex = ex - ax add ax, 2 ;ax=ax+2 eero: emp ex, ax ; si ex > = ax, jge /azol ; saltar a /azol sar mov ax, 1 ex, ax ; ax = nro. enteros imp. ; ra[z aproximada imu/ add ine emp jg ine fin: J ex ax, ex ax ax, bx fin ex ;ax ex*ex ; ax ax + ex ;ax ax+l ; eomparar con n ; saltar si > ; si >, ex = ex + 1
  • 613. Una funci6n C definida por el usuario 0 perteneciente alas librerias estan- dar, puede ser llamada des de un bloque _asm utilizando la instrucci6n call. En general, cuando se llama a una funci6n los parametros son pasa- dos a la pila (de derecha a izquierda bajo el convenio _cdecl) para ser re- cuperados de la misma cuando la fund6n se ejecuta. Esto nos indica la forma de pro ceder para llamar a una fund6n desde un bloque _asm: 1. Salvar los parametros en la pila, empezando par el que esta mas a la derecha (push). char formato[ J = "%s = %d n"; char *cadena = "Reg. AX"; main( ) { int x = 10, y z=x+y; _asm { ; visualizar el valor de ax movax, Z push ax mov ax, cadena push ax mov ax, offset formato push ax
  • 614. call printj mov cars, ax ; llamada a la funci6n ; valor retornado por printj } printj("caracteres escritos } El cuerpo de una funci6n C puede ser reemplazado por un bloque _asm. Esto es uti! cuando en ocasiones necesitamos una mayor velocidad de eje- cuci6n. El siguiente ejemplo inicializa todos los elementos de un array a un valor determinado. / * Inicializar un array */ # include <stdio.h> main( ) ( int inicializar(int *v); int vector[250], i; / *prototipo */ / * declaraci6n del array */ inicializar(vector ); for (i = 0; i < 250; i+ +) printjt'%d ': vector[i]); / * llamada a la funci6n */ / * escribir el array */ / * Esta funci6n inicializa el array apuntado por v */ int inicializar(int *v) { mov di, v movax, ds moves, ax movax,96h ;-direcci6n de comienzo del array ; poner la direcci6n del segmento de ; datos, en "es': utilizado por stosw ; valor de inicializaci6n (150)
  • 615. movex, Oxfa cld rep stosw ; numero de elementos (lei array (250) ; poner el pag de direeci6n a 0 ; eseribir "ax" en eada elemento Normalmente las interrupciones del DOS y del BIOS son procesadas utili- zando las funciones int86( ) 0 int86x( ) como veremos en el capitulo si- guiente. No obstante, como ya hemos visto en algunos ejemplos en este mis- mo capitulo, es muy facil Hamar a una rutina de interrupci6n desde un bloque _asm utilizando la instrucci6n into El ejemplo que se muestra a continuaci6n indica de una forma sencilla c6mo manipular punteros _based y punteros -far. / * Utilizaci6n de punteros en bloques _asm */ / * Compi/ado bajo el modelo small */ # include <stdio.h> # include <string.h > # include <malloe.h > char .-far *invertir(char _far *Str),o -segment segmento1, seg_data,o char _based(segmento1) *peadena,o int main(void) { char eadena[81], _far *resu,o if ((segmento1 = _bheapseg(2048)) = = -.NULLSEG) { puts("error 1: segmento no asignado"),o exit(l),o
  • 616. if ((pcadena = _bmalloc(segmento1, 81)) = = ---.NULLOFF) ( puts(Herror 3: insujiciente espacio de memoria"); exit(3); _asm {mov seg_data, ds } printj(Hseg_data = %p n': seg_data); printf(Hsegmento 1 = %p n': segmento1); printf(Hpcadena = %fp n': (char -far *)pcadena); while (1) { printf(H n nIntroducir cadena (0 FIN): "); gets(cadena); if (!stricmp(cadena, HFIN")) break; -fstrcpy((char _far *)pcadena, (char _far *)cadena); printf(H%Fs n': (char _far *)pcadena); resu = invertir(pcadena); printj(H n%Fs': resu); } / * Liberar memoria */ _bjreeseg(segmento1); _bjree(segmento1, pcadena); } char ~ar *invertir(char ~ar *pcad) { fes bx, pcad ; transjiere puntero de 32 bits sub cx, cx ; 0 = cardcter de terminaci6n meter: push cx ; meter caracteres en fa pi/a mov c/, es:[bx] ; coger un cardcter de memoria jcxz inver , inc bx ; siguiente cardcter jmp meter , inver: fes bx, pcad , sacar: pop cx ; sacar caracteres de fa pi/a
  • 617. mov jcxz inc jmp les mov es:[bx], cl fin bx sacar ax, pcad dx, es ,. devolver desplazamiento ,. devolver segmento Este ejemplo lee una cadena, la almacena en un segmento denomina- do segmentol, la invierte, utilizando para ello la funci6n invertir( ) y escri- be el resultado obtenido. Observar en la funci6n main( ) c6mo se obtiene el valor del registro DS utilizando un bloque _asm. La funci6n invertir( ) ha side disefia'da para devolver un puntero far (segun el convenio C, en DX:AX). Observar tambien que un puntero based ocupa dos bytes; cuando es pasado a la fun- ci6n 0 cuando es devuelto por la funci6n, es tratado como un puntero far; 10 que indica que tanto el segmento como el desplazamiento de la direc- ci6n de memoria son pasados y retornados. Un miembro de una estructura 0 de una uni6n puede referenciarse dentro de un bloque _asm, utilizando el nombre de la estructura y el nombre del miembro (ademas del operador punto) 0 utilizando solamente el nombre del miembro. Por ejemplo: mov bx, offset estr mov ax, [bx].miembro Si el nombre del miembro aparece en mas de una estructura, es nece- sario especificar el nombre de la estructura para evitar ambiguedades. Por ejemplo:
  • 618. mov bx, offset estr mov ax, estr[bx}.miembro El ejempl0 siguiente muestra como acceder desde un bloque _asm, a 10s miembros de una estructura definida en C. Concretamente, este ejemplo define dos estructuras: s1 y s2 asigna valores a la estructura s2 y copia s2 en s1. main( ) { static struct ts1 { int m1, m2, sig; }; static struct ts2 { char car; struct ts1 f; struct ts2 *-Sig; }; / * Copiar fa estruetura s2 en fa estruetura sl */ _asm { sub ex, ex mov cl, s2.ear mov sl.ear, cl mov ex, s2jm1 mov sljm1, ex mov ex, s2jm2 mov sljm2, ex
  • 619. mov ex, s2.j.sig mov sl.j.sig, ex mov bx, offset s2 mov ex, s2.sig mov bx, offset sl mov sI.sig, ex printf(Hearcieter = %e n': sI.ear); printftj = %d-%d-%d n': sl.j.ml, sl.j.m2, sl.j.sig); printf(Hdireeci6n siguiente = %p n': sI.sig); J Observar que el bloque _asm trabaja correctamente porque las es- tructuras se han definido estaticas (almacenadas en el segmento de datos, DS). Si hubieran sido declaradas automaticas (aulo), estarian almacena- das en la pila; esto exigiria trabajar utilizando desplazamientos negativos relativos al registro BP. _asm { mov di, offset s2 mov si, offset sl sub ex, ex mov cl, [di].ear mov [sij.ear, cl mov ex, [dij.j.ml mov [sij.j.ml, ex mov ex, [dij.j.m2 mov [sij.j.m2, ex mov ex, [dij.j.sig mov [sij.j.sig, ex mov ex, s2[dij.sig mov sl[sij.sig, ex J Si en lugar de estructuras definimos punteros a estructuras, el bloquc _asm no cambia.
  • 620. Las instrucciones de bifurcaci6n en ensamblador y en C pueden utilizarse para saltar a una etiqueta dentro de un bloque _asm 0 fuera de el. Cuan- do se define una etiqueta en un bloque _asm no hay diferencia entre utili- zar mayusculas 0 minusculas. En cambio, cuando se define una etiqueta C sf existe esta diferencia. main( ) { goto et_c,· goto et_c; II correcto II error: etiqueta no dejinida goto et_asm; goto et----.ASM; _asm ( imp et_C imp et_c II correcto II correcto II correcto II error: etiqueta no dejinida imp et_asm imp et----.ASM II correcto II correcto printf(HFIN n "); l
  • 621. Un programa que contenga bloques _asm puede ser depurado con Code View, sin ninguna restricci6n (ver el depurador Code View en el capitulo anterior). La presencia de un bloque _asm afecta a la optimizaci6n del progra- ma en las formas siguientes: • Como cabe esperar, un bloque _asm no es optimizado. El compi- lador respeta el c6digo escrito en ensamblador. • Bajo circunstancias normales el compilador automaticamente alma- cena las variables en los registros. Cuando una funci6n contiene un bloque _asm esto no ocurre a no ser que se indique explicitamente con la palabra clave register. Finalmente, cuando una funci6n contiene un bloque _asm, quedan inhibidas en toda la funci6n las siguientes optimizaciones: Para poder enlazar un procedimiento escrito en lenguaje ensamblador con un programa C deben utilizarse segmentos compatibles. Los siguientes pun- tos pueden ser de gran ayuda: 2. Utilizar las directrices .CODE y .DATA para declarar los segmen- tos de c6digo y de datos respectivamente. (Con las versiones 5.0 o posteriores del macroensamblador de Microsoft utilizar el for- mato simplificado).
  • 622. 3. EI nombre del procedimiento debe ser declarado con la directriz PUBLIC. Tambien debe ser declarado PUBLIC cualquier otro data que se quiera utilizar desde otro modulo. 4. Los procedimientos y los datos accedidos per la rutina en ensam- blador deben ser declarados EXTERN. La forma mas segura de utilizar EXTERN es colocar la directriz fuera de cualquier defini- cion de segmento. push bp ;salvar bp mov bp, sp ;preparar para acceder a pardmetros El registro BP es utilizado para acceder a los parametros y a los datos locales almacenados en la pila (stack). EI registro SP no puede utilizarse para este proposito porque no es un registro indice 0 base. Por otra parte, SP cambia cuando se meten mas datos en la pila, mientras que el registro BP permanece constante; asi que cada parametro puede ser direccionado con un desplazamiento con respecto a BP. EI valor de BP es salvado porque es necesario cuando termine la eje- cucion del procedimiento para poder continuar con la ejecucion del programa. Un procedimiento llamado debe salvar los valores de los registros SI, DI, SS Y DS ademas del BP que ya ha side salvado. Esto es: push bp mov bp, sp push si push di push ss push ds
  • 623. Cuando salgamos del procedimiento, estos registros deben ser restau- rados en el siguiente orden: pop ds pop ss pop di pop SI pop bp ret 1. La Hamada des de el program a pone en la pila cad a uno de los pa- nimetros. 2. A continuaci6n se salva en la pila la direcci6n de retorno al pro- grama. Esta direcci6n puede ser de 2 bytes (near) 0 de 4 bytes (far). 3. Despues el procedimiento salva el registro BP. 4. Por ultimo se salvan los registros SI, Dl etc. panimetro panimetro panimetro direcci6n de retorno BP SI DI
  • 624. Esto qui ere decir que en el caso de una Hamada (near) a un procedi- miento que ha recibido un solo panimetro, dicho panimetro se encuentra localizado en la direccion BP + 4. Al salvar los datos en la pila siempre se pone la direccion del segmen- to antes que la direccion offset (desplazamiento) y con los argumentos su- peri ores a dos bytes, se pone siempre la palabra alta antes que la baja. Dependiendo del tamafio en bytes del valor retornado, este es devuelto en los registros: 1 byte 2 bytes 4 bytes AL AX Parte alta (0 direccion del segmento) en DX Parte baja (0 direccion offset) en AX Si el valor retornado es mayor de 4 bytes, el procedimiento Hamada por C debe asignar espacio para el valor a devolver y entonces colocar su direccion en DX:AX. Una forma simple de asignar espacio para el valor a devolver es declararlo en el segmento de datos. Un programa C puede Hamar a un procedimiento en ensamblador almace- nado en otro modulo, igual que Hama a una funcion. Los siguientes pasos pueden servir de ayuda: 1. Declarar NEAR el procedimiento Hamado des de C, si el modulo es compilado bajo uno de los modelos small 0 compact; declarar-
  • 625. 10 FAR, si este es compilado bajo uno de los modelos large, huge o medium. 3. Los panimetros son colocados en la pila en orden inverso a como aparecen en la llamada (de derecha a izquierda). 4. Por defecto los panimetros son pasados por valor, excepto los arrays que son pasados por referencia. 5. Incluir un gui6n bajo delante de cualquier nombre que vaya a ser com partido con C. Solamente los ocho primeros caracteres son reconocidos. 6. Si al efectuar el enlace se utiliza la opci6n INOI, C no convierte los nombres a mayusculas. Ensamblar con la opci6n IMX para prevenir que MASM convierta los nombres a mayusculas. Realizar el mismo programa anterior del calculo de la raiz cuadrada de un numero entero, pero ahora utilizando un m6dulo separado en en- samblador. main( ) [ printjt~ mlmero ? "); scanft'%d': &numero); printj("%d': sqrte(numero)); l Este programa, llama para su ejecuci6n al procedimiento que se des- cribe a continuaci6n, editado bajo el nombre de SQRTE.ASM, que es una rutina para calcular la raiz cuadrada entera de un numero entero.
  • 626. .MODEL SMALL .CODE ---.Sqrte ,. Entrada: ex ,. Salida : ax PUBLIC ---.Sqrte PROC push bp mov bp, mov ex, mov mov jmp [bp+4] bx, ex ax, 1 SHORT eero ,. salvar bp ,.preparar para aeeedera ,.pardmetros ,. eargar el argumento ,. haeer una eopia ,.primer mimero impar , <: n <= 0 ? lazol: sub ex, ax ,. ex = ex - ax add ax, 2 ,. ax = ax + 2 eero: emp ex, ax ,. si ex > = ax, jge lazol ,. saltar a lazol ax, 1 ,. ax = nro. enteros imp. mov ex, ax ,. razz aproximada imul ex ,. ax = ex * ex add ax, ex ,. ax = ax + ex ine ax ,. ax = ax + 1 emp ax, bx ,. eomparar eon n jg fin ,. saltar si > ine ex ,. si >, ex = ex + 1 mov ax, ex pop bp ret ENDP END
  • 627. Una vez editados los dos modulos anteriores, proceder de la forma siguiente: C: > MASM IMX SQRTE.ASM C: >CL RAIZ.C SQRTE.OBJ C:>RAIZ C: > CL RAIZ.C IMAMX SQRTE.ASM C:>RAIZ se obtiene SQRTE.OBJ se obtiene RAIZ.EXE ejecutar RAIZ se obtiene RAIZ.EXE ejecutar RAIZ
  • 628. COMUNICACIONES. SERVICIOS DEL DOS Y DEL BIOS El DOS dispone de numerosas rutinas que nosotros podemos utilizar acti- vando la interrupci6n correspondiente. De todas ellas, la interrupci6n 21H tiene un especial interes, ya que nos permite acceder a todos los servicios del DOS. (Para sacar provecho a este capitulo debe conocerse la arquitec- tura del microprocesador 8086 y las interrupciones y servicios de DOS). C tambien dispone de funciones que nos van a permitir realizar la in- terrupci6n requerida, y ejecutar asi la rutina deseada del DOS. Las funcio- nes que se estudian en este capitulo utilizan las uniones y estructuras que se presentan a continuaci6n y que estin declaradas en el fichero dos.h~ union REGS { struct WORDREGS x; struct BYTEREGS h; Estructura: struct WORDREGS { unsigned int ax;
  • 629. unsigned int bx; unsigned int ex; unsigned'int dx; unsigned int si; unsigned int di; unsigned int ejlag; Estructura: struct BYTEREGS { unsigned char ai, ah; unsigned char bl, bh; unsigned char c/, eh; unsigned char dl, dh; Estructura: struct SREGS ( unsigned int es; unsigned int es; unsigned int ss; unsigned int ds; Las declaraciones para las funciones que se detaIlan a continuacion estan contenidas en el fichero dos.h. Esta funcion es utilizada para realizar una interrupcion directamente al DOS y ejecutar la rutina correspondiente. La funcion int86( ) ejecuta la interrupcion especificada por n_int. An- tes de ejecutarse la interrupcion, int86( ) copia el contenido de in_regs en 10sregistros correspondientes. Despues de ejecutarse la itlterrupcion, int86()
  • 630. copia los valores actuales de los registros en out_regs. Tambien copia el registro de flags en el campo cflag de out_regs. No utilizar la funci6n int86( ) para interrupciones que modifiquen el registro DS. En su lugar utilizar la funci6n int86x( ) 0 la funci6n intdosx( ). EI valor devuelto por esta funci6n es el valor del registro AX despues de la interrupci6n. Si el valor del campo cflag es distinto de 0, quiere decir que ha ocurrido un error y la variable _doserrno es puesta al valor corres- pondiente. Esta funci6n es utilizada para realizar una interrupci6n directamente al DOS y ejecutar una rutina que recibe un argumento en el registro ES, 0 que reci- be en el registro DS un valor diferente del segmento de datos por defecto. int int86x(int n_int, union REGS *in~regs, union REGS *out_regs, struct SREGS *regs---.Seg); La funci6n int86x( ) ejecuta la interrupci6n especificada por n_int. Antes de ejecutarse la interrupci6n, int86x( ) copia el contenido de in_regs y de regs---.Segen los registros correspondientes. De regs---.Seg,solamente son utilizados los valores de los registros DS yES. Despues de ejecutarse la interrupci6n, int86x( ) copia los valores actuales de los registros en out_regs y los valores actuales de DS y ES en regs---.Seg,restaurando DS. Tambien copia el registro de flags en el campo cflag de out_regs. EI valor devuelto por esta funci6n es el valor del registro AX despues de la interrupci6n. Si el valor del campo cflag es distinto de 0, quiere decir que ha ocurrido un error y la variable _doserrno es puesta al valor corres- pondiente. Esta funci6n es utilizada para realizar una Hamada al DOS y ejecutar una rutina que reciba argumentos en otros registros distintos de DX (DH/DL) y AL, 0 para indicar errores en funci6n de los flags.
  • 631. La fundon intdos( ) llama a la rutina del DOS especificada por los valores de los registros en in-,"egs. Para efectuar la llamada, esta funci6n ejecuta la instruccion INT 21H. Antes de ejecutarse la instrucdon, intdos( ) copia el contenido de in-,"egs en los registros correspondientes. Despues de ejecutarse la instruccion INT 21H, la fundon copia los valores actuales de los registros en out-,"egs. Tambien copia el registro de flags en el cam- po cflag de out-,"egs. No utilizar la funcion intdos( ) para interrupdones que modifiquen el registro DS. En su lugar utilizar la fundon int86x( ) 0 la funcion int- dosx( ). El valor devuelto por esta funcion es el valor del registro AX despues de la interrupcion. Si el valor del campo cflag es distinto de 0, qui ere decir que ha ocurrido un error y la variable _doserrno es puesta al valor corres- pondiente. Esta funcion es utilizada para realizar una llamada al DOS y ejecutar una rutina que reciba un argumento en el registro ES, 0 que redba en el regis- tro DS un valor diferente del segmento de datos por defecto. int intdosx(union REGS *in-,"egs, union REGS *out_regs, struct SREGS *regs........seg); La funcion intdosx( ) llama a la rutina del DOS espedficada por los valores de los registros en in-,"egs. Para efectuar la llamada esta funci6n ejecuta la instrucdon INT 21H. Antes de ejecutarse la instrucdon, la fun- cion intdosx( ) copia el contenido de in-,"egs y de regs........segen los regis- tros correspondientes. De regs........seg,solamente son utilizados los valores de los registros DS yES. Despues de ejecutarse la instrucdon INT 21D, la fundon copia los valores actuales de los registros en out-,"egs y los va- lores actuales de DS yES en regs........seg,restaurando DS. Tambien copia el registro de flags en el campo cflag de out_regs.
  • 632. El valor devuelto por esta funci6n es el valor del registro AX despues de la interrupci6n. Si el valor del campo c/lag es distinto de 0, quiere decir que ha ocurrido un error y la variable _doserrno es puesta al valor corres- pondiente. Esta funci6n copia en una estructura de tipo SREGS apuntada por regs_seg el contenido de los registros de segmentos. Esta funci6n llama al DOS para ejecutar una rutina especificada par fo_dos despues de copiar los contenidos de dos_dx y dos_a) en los registros DX y AL respectivamente. Para efectuar la llamada esta funci6n ejecuta la ins- trucci6n INT 21D. La funci6n bdos() se utiliza para realizar llamadas al DOS yejecutar rutinas que no tomen argumentos 0 que solamente los tomen en los regis- tros DX (DH,DL) y/o AL. No utilizar la funci6n bdos( ) para interrupciones que modifiquen el registro DS. En su lugar utilizar la funci6n int86x( ) 0 la funci6n intdosx( ). El valor devuelto por esta funci6n es el valor del registro AX despues de la llamada. Esta macro es utilizada para copiar 0 recuperar el offset de la direcci6n far especificada por puotero.
  • 633. Esta macro es utilizada para copiar 0 recuperar el segmento de la direcci6n far especificada por puntero. La macro FP _SEG devuelve como resultado el segmento de la direc- cion far. # include <dos.h > # include <stdio.h> # include <stdlib.h> # include <conio.h > # define DC1 Oxll #define DC3 Ox13 #define INIT 0 #define SEND 1 #define READ 2 #define STAT 3 #define n 81 main( ) ( void rs232_inic( ); /* XON d /* XOFF */ / * Funcion 0 de fa interrupcion 14 */ / * Funcion 1 de fa interrupcion 14 */ / * Funcion 2 de fa interrupcion 14 d / * Funcion 3 de fa interrupcion 14 */ / * Longitud maxima de fa cadena d
  • 634. void rs232_lect( ); void rs232-----stat(); void rs232_envi( ); / * Espera por un cardcter de COM */ / * estado de COM */ / * Enviar cardcter XON 0 XOFF */ FILE *pj; char c, cadena[n}; int i = 0; if ((pj = jopen("datos': "w")) = = NULL) { printjt'Error: El jichero datos no se puede abrir"); exit(I); l rs232_inic( ); systemt 'els' '); printj("Recepci6n de datos. Pulse una teela para jinalizar"); while ( 1) { / * Pulse una teela para jinalizar la recepci6n */ if (kbhit( ) != 0) exit(O); rs232-----stat(); if ((v2.h.ah & 1) = = 1) { rs232_lect( ); cadena[i + +} = v2.h.al,· if (i = = n) { vJ.h.al = DC3; / * enviar XOFF para detener */ rs232_envi( ); / * la transmisi6n */ jwrite(cadena, sizeof(cadena), 1, pj); i = 0; vJ.h.al = DC1; rs232_envi( ); l l l l / * leer el cardcter */ / * almacenarlo */ / * enviar XON para continuar */ / * la transmisi6n */
  • 635. void rs232_inic( ) { v1.x.dx = 0; v1.h.al = Oxfe; v1.h.ah = INIT; int86 (Ox14,&v1, &v2); / J / * seleccionar puerto COM1 */ / * seleccionar: Baudios, Paridad, * bit Stop y Longitud palabra * ( BBBPPSLL) */ / *funci6n (0) de int 14 a realizar */ * llamada a la funci6n */ void rs232_lect( ) ( v1.h.ah = READ; int86(20, &v1, &v2); J / *funci6n (2) de int 14 a realizar */ / * llamada a la funci6n */ void rs232----stat() ( v1.h.ah = STAT; int86(20, &v1, &v2); J hfunci6n (3) de int 14 a realizar */ / * llamada a la funci6n */ void rs232_envi( ) ( v1.h.ah = SEND; int86(20, &v1, &v2); J / * funci6n (1) de int 14 a realizar */ / * llamada a la funci6n */ # include <dos.h> # include <stdio.h> # include <stdlib.h>
  • 636. union REGS in~egs, out~egs; struct SREGS regs~eg; main( ) ( char ~ar *buffer= "Finalizar cadenas con signo dolar n r n r$"; char ~ar *P; systemt<c!s"); / * Obtener la version de DOS utilizando la fun cion DOS Ox30. */ in~egs.h.ah = Ox30; intdos( &in~egs, &out~egs ); printj(" nDOS version %d.%d n n' :out~egs.h.al,out~egs.h.ah); / * Escribir una cadena de caracteres finalizada con el signo $, * utilizando la funcion DOS Ox9. */ in~egs.h.ah = Ox9; in~egs.x.dx = FP_OFF( buffer ); regs~eg.ds = FP~EG( buffer ); intdosx( &in~egs, &out~egs, &regs~eg ); segread( &regs~eg ); printj( "Segmentos: n tCS t%.4x n tDS t%.4x n tES t%.4x n tSS t%.4x n n'; regs~eg.cs, regs~eg.ds, regs---..seg.es,regs~eg.ss ); / * Asegurarse de que la impresora estd disponible. * No estd disponible si cualquier bit de error estd en ON * 0 si uno 0 ambos bits de operacion estdn en OFF. */ in~egs.h.ah = Ox2; / *funcion 2 (status) de la int Ox17 */ in~egs.x.dx = LPT1; / * impresora 0 */ int86( Ox17,&in~egs, &out~egs ); iff (out~egs.h.ah & Ox29) II !(out~egs.h.ah & Ox80) II !(out~egs.h.ah & OxlO)) printj( "Impresora no disponible." ); else { / * Escribir una cadena en la impresora utilizando la
  • 637. *funci6n DOS Ox5. */ for( p = buffer; *P /= '$'; P+ + ) bdos( Ox05, *P, 0 ); / * Escribir ef contenido de fa pantalla. */ in_regs.h.ah = Ox05; int86( Ox05, &in_regs, &out_regs ); ] ] C dispone de un conjunto de funciones que permiten acceder alas rutinas basicas de entrada-salida almacenadas en la ROM (ROM-BIOS). La estructura utilizada para enviar y recibir informacion a y desde 105 servicios del disco es: struct diskinfo_t ( unsigned drive; unsigned head; unsigned track; unsigned sector; unsigned nsectors; void far *buffer; La estructura diskinfo_t contiene los datos necesarios para ejecutar un servicio. Hay seis servicios basicos:
  • 638. Inicializa la unidad de disco y su controla- dor. Para este servicio el panimetro info es ignorado. Informa del estado de la ultima operaci6n de la unidad de disco. Para este servicio el pa- nimetro info es ignorado. Lee uno 0 mas sectores del disco, especifica- dos en la estructura info, y los almacena en la zona de memoria apuntada por buffer. Escribe uno 0 mas sectores en el disco, espe- cificados en la estructura info, con la infor- maci6n almacenada en buffer. Verifica uno 0 mas sectores del disco para ase- gurar que los datos escritos son perfectamente recuperables, 10 que significa comprobar la paridad y otros defectos de grabaci6n. Tam- bien hace un CRC (cyclic redundancy check). No comprueba si los datos almacenados coin- ciden con los de la memoria. Se utiliza para formatear una pista del dis- co, la especificada por los parametros head y track. Este es el formate ffsico que sirve de base al formate 16gico del DOS. EI valor retornado depende del servicio. Los servicios reset y format no retornan un valor significativo. Los servicios read, write y verify retor- nan el numero de sectores procesados en el byte de menor peso. Si ocurre un error, el servicio status coloca el c6digo correspondiente en el byte de mayor peso. Un 0 en este byte significa que no se ha producido un error.
  • 639. La configuraci6n del equipo se obtiene con la interrupci6n 17 (INT Oxll) que esta obsoleta ya que entre otras cosas no da el tamafio de la memoria. 15,14 13 12 11,10,9 8 7,6 5,4 3,2 1 o numero de puertos paralelo 1 si hay un modem instalado 1 si hay un adaptador de juegos numero de puertos serie o si hay un DMA (chip) instalado unidades de disco flexible (0 a 4) modo de video (00 no utilizado, 01 40x25 color, 10 80x25 color, 11 monocromo) memoria RAM en bloques de 16K (11=64k) 1 si hay un coprocesador instalado 1 si hay unidades de disco Esta funci6n devuelve una palabra de 16 bits indicando 10 que hay ins- talado.
  • 640. Lee el siguiente canicter desde el buffer de entrada del teclado. Si no hay un canicter, espera por el. Informa si hay algun canicter en el buffer de entrada del teclado. EI canicter se queda en el buffer hasta que se lea con el servicio O. Comunica los bits de estado del teclado, que indican el estado de las teclas de tipo shift (si se ha pulsado una tecla Shift, Alt, Ctrl, etc.). Esta funci6n retorna un valor que depende del servicio. Si el servicio es 0 retorna en el byte de menor peso el valor ASCII del canicter y en el byte de mayor peso el c6digo del teclado (scan code). Si el byte de menor peso es 0 la tecla pulsada corresponde a una tecla para el movimiento del cursor 0 a una tecla de funci6n. Si el servicio es 1 retorna un 0 si el buffer esta vacio, si no retorna el siguiente caracter en el buffer. Si el servicio es 2 retorna el estado de las teclas tipo shift cuando estan activadas (1 Shift derecho, 2 Shift izquierdo, 4 Ctrl, 8 Alt, 16 Scroll Lock, 32 Num Lock, 64 Caps Lock, 128 Ins). Permite conocer la cantidad de memoria que tiene el ordenador. Este ser- vicio se activa con la interrupci6n 18 (INT OxI2). Retorna el maximo numero de bloques contiguos de memoria de lK presentes.
  • 641. unsigned _bios_printer(unsigned servicio, unsigned impresora, unsigned dato); Manda un unico octeto a la impresora el cual se corres- ponde con el byte de menor peso de dato. Informa del estado de la impresora. EI argumento dato es ignorado. EI byte de menor peso del valor retornado conti ene, para todos los servicios, el estado de la impresora (1: impresora apagada, 8: error de E/S, 16: impresora seleccionada, 32: no hay papel). # include <stdio.h> # include <bios.h > main( ) { unsigned vr, dato = 0; enum {LPn, LPT2, LPT3, PRN=O}; enum {ESCRIBIR, INICIALIZAR, ESTADO };
  • 642. vr = _bios--printer(ESTADO, LPT1, dato),o printj("Impresora LPT1, estado: % # 04x n'~ vr),o mensaje_error(vr & OxOOFF),o vr = _bios--printer(INICIALIZAR, LPT1, dato),o printj(" nLPT1 inicializada, estado: % #04x n'~ vr),o mensaje_error(vr & OxOOFF),o dato = '/',o vr = _bios--printer(ESCRIBIR, LPT1, dato),o printj(" nLPT1 trabajando, estado: % # 04x n'~ vr),o mensaje_error(vr & OxOOFF),o ] void mensaje_error(unsigned vr) [ char *msg[ J= [ "bit 0: impresora apagada'~ "bit 1: no utilizado'~ "bit 2: no utilizado'~ "bit 3: error de E/S'~ "bit 4: impresora sefeccionada'~ "bit 5: no hay papel'~ "bit 6: respuesta de fa impresora': "bit 7: impresora no ocupada" ]; int i; unsigned test = Ox0001; if (vr != 0) for (i = 0; i < 8; i+ +) if (vr & (test < < i)) printj("%s n'~ msg[i]); Impresora LPTl, estado: Ox30 bit 4: impresora seleccionada bit 5: no hay papel LPTI inicializada, estado: Ox30 bit 4: impresora seleccionada
  • 643. LPTl trabajando, estado: Ox31 bit 0: impresora apagada bit 4: impresora seleccionada bit 5: no hay pape1 unsigned _bios_serialcom(unsigned servicio, unsigned puerto, unsigned dato); Inicializa el puerto de comunicaciones (velocidad en baudios, longitud del dato, bits de parada y paridad) dato Baudios Bits dato Bits Stop Paridad 0 110 ninguna 2 7 3 8 4 2
  • 644. dato Baudios Bits dato Bits Stop Paridad 8 impar 24 par 32 150 64 300 96 600 128 1200 160 2400 192 4800 224 9600 unsigned r, data = 0; enum ( COM1, COM2, COM3 ]; data = 160 I 2 I 4 I 0; r = _bias---.Serialcam(O, COM1, data); Este ejemplo inicializa el puerto 1con los siguientes valores: 2400 bau- dios, dato de 7 bits, 2 bits de stop y no paridad. Esta funci6n retorna un entero. El byte de mayor peso, contiene infor- maci6n de estado y el byte de menor peso depende del servicio. Cad a bit del byte de mayor peso indica: registro de desplazamiento de transmisi6n, vado registro de datos, vado interrupci6n detectada (break) error de transmisi6n (encuadre) error de paridad error de transmisi6n (se han traspasado los limites) dato listo
  • 645. Si el servicio es 1, el bit 15 seria puesto a 1 si el dato no pudiera ser enviado. Si el servicio es 2, el dato leido seria devuelto en el iJyte de menor peso. Si ocurre un error, al menos un bit del byte de mayor peso seria puesto a 1 para indicar la procedencia del error. Si el servicio es 0 0 3, en el byte de menor peso se da una informacion adicional. DCD (detecci6n de portadora de datos) RI (tonos de Hamada en linea) DSR (datos preparados) CTS (listo para enviar) Cambio en DCD Cambio en RI Cambio en DSR Cambio en CTS Los servicios con respecto a la hora del dia se activan con la interrupcion 26 (INT Oxla). Hay dos servicios: 0 para leer la hora y 1 para poner la hora. La hora hay que especificarla en pulsos de reloj transcurridos desde la 0 horas. Un segundo equivale aproximadamente a 18.2,Pulsos de reloj. Ejemplo: # include <stdio.h> # include <bios.h >
  • 646. main( ) { static char bujjer[1024]; void _far *p = (char _far *)bujjer; struct diskinjo_t disco = { 0, 0, 0, 1, 1, p ]; int i, valor, minutos, horas; long tiempo; for (i = 0; i < 4; i+ +) { disco.drive = i; _bios_disk(O, &disco); if ((valor = _bios_disk(2, &disco)) & OxFFOO) printf(Herror: E/S disco %d - estado: % # 06x n': i, valor); valor = _bios_equiplist( ); printf(H nn 0 de puertos serie: %d n': (valor & OxOeOO)> > 9); printf(H npulse una tecla para continuar n "); valor = _bios_keybrd(O); if (valor & OxOOjf) [ printf(H ncardcter introducido %c n': valor & OxOOjj); printf(Hc6digo de teclado %d n': (valor & OxjjOO)> > 8); I _bios_timeojday(O, &tiempo); tiempo = (tiempo*10)/182; minutos = tiempo/60; horas = minutos/60; minutos % = 60; printf(H nhora: % 02d:%02d n': horas, minutos); ]
  • 647. error: E/S disco 1 - estado: Ox8000 error: E/S disco 2 - estado: Ox0101 error: E/S disco 3 - estado: Ox0101 canicter introducido c6digo de teclado 57
  • 648. C permite ejecutar funciones relacionadas con el sistema operativo como chdir( ), mkdir( ), rmdir( ), getcwd( ) y system ( ). De estas, tiene especial interes la funcion system ( ) ya que nos permite ejecutar cualquier fichero .COM, .EXE 0 .BAT, con 10 que tenemos acceso desde un pragrama C a cualquier orden del DOS. Cuando se da formate a un disco desde el DOS, se crea un directorio que recibe el nombre de directorio raiz ( ). Las entradas en este directorio pue- den ser ficheras 0 bien otros directorios (llamados subdirectorios). Estos ultimos, a su vez, pueden contener ficheros 0 bien subdirectorios, y asi su- cesivamente. Es una secuencia de uno 0 mas nombres de directorios, separados por . Esta secuencia puede finalizar con un nombre de fichera. La sintaxis es:
  • 649. El primer canicter es el nombre del directorio raiz. El directorio activo puede ser referenciado por "." y el directorio padre del activo por "..". Se denomina directorio activo al directorio en el que estamos traba- jando. Se dice que un directorio A es padre de otro B, si B es subdirectorio de A. La siguiente figura (arbol) muestra un ejemplo de como puede ser la estructura de ficheros de un disco. (directorio raiz) I En esta figura hemos representado los directorios con mayusculas y 10s ficheros con minusculas. C es el directorio padre de los subdirectorios FICHEXE, FICHINC y EJEMPWS. Suponiendo que estamos en la unidad C, el camino para acceder al fichero ejOl.c es:
  • 650. Este otro camino conduce hasta el directorio C. Se asume la unidad de disco en la que estamos trabajando. El siguiente camino conduce hasta el directorio padre del activo. Por ejemplo, si estamos en C, nos lleva hasta el directorio rafz. Significa que se puede enviar la informacion producida por una orden cual- quiera, no solamente a la salida estandar, sino a un fichero cualquiera, uti- lizando el sfmbolo > en la linea de ordenes. Por ejemplo: Esta orden crea el fichero INDICE si no existe, y almacena la infor- macion suministrada por la orden DIR en el. Con esta orden grabamos la informacion suministrada por DIR, a con- tinuacion de la ya existente en INDICE. Es posible obtener la informacion de entrada para una orden no solamen- te del teclado, sino de un fichero cualquiera utilizando el sfmbolo < en la linea de ordenes. Por ejemplo: Con esta orden clasificamos los datos del fichero ALUMNOS y el re- sultado es enviado al fichero ALUM.CLA.
  • 651. Cuando queremos procesar el resultado de una orden con otra orden se utiliza el simbolo I (ASCII 124). Por ejemplo: Esta linea de 6rdenes visualiza en pantalla el resultado dado por la orden DIR (directorio) clasificado en orden alfabetico. Se denomina prompt al indicativo que se visualiza en pantalla cuando se tiene cargado el sistema operativo. Por defecto, el prompt esta formada por el nombre de la unidad de disco en la que estamos trabajando y el sim- bolo >. A > indica que estamos en la unidad A B> indica que estamos en la unidad B C> indica que estamos en la unidad C Para ir de una unidad a otra, se escribe el nombre de la unidad a la que queremos ir seguida de dos puntos y se puisa Enter. Por ejemplo: Ademas de saber en que unidad estamos, es importante saber tambien en que directorio de esa unidad nos encontramos trabajando. La orden del DOS que nos facilita permanentemente esta informaci6n es: Por ejemplo, de acuerdo con la estructura en arbol presentada ante- riormente y despues de haber ejecutado PROMPT $p$g, si estamos en el directorio EJEMPLOS de la unidad C, el prompt que se visualiza en pan- talla es:
  • 652. Observar el prompt de estos ejemplos (directorio en el que estamos) para comprender la orden que se quiere ejecutar. Cuando se quiere pasar un nombre de fichero (path-name) a un proceso en una Hamada exec( ) 0 en una funci6n, como par ejemplo la funci6n system(), se debe utilizar como delimitador una doble barra invertida ( ) en lugar de una sola barra invertida (). r = system("DIR C: C600 SOURCE"); execl("bin/prog': "prog': "sub': "bin xxx': NULL);
  • 653. EI sistema operativo UNIX utiliza como delimitador I en lugar de que utiliza.MS-DOS. No obstante, MS-DOS reconoce como delimitador / en aquellos lugares donde se espera un path. En un lugar donde no es es- perado hay que utilizar . Ellenguaie C dispone de las funciones que a continuaci6n se exponen para control de directorios: # include <direct.h > # include <errno.h > La funci6n chdir( ) devuelve el valor 0 si se ejecuta satisfactoriamente y un valor -1 si ocurre un error. Este ejemplo, suponiendo que estamos en la unidad B, conduce hasta el directorio PROGINC de la unidad de disco B. Tambien podria haberse especificado el camino HB:IPROGINC': main( ) { if ( chdir(Hc: c jichexe") 0 ) system(Hdir"); else printft tError' ');
  • 654. Este ejemplo conduce hasta el directorio FICHEXE de la unidad C y lista su contenido. # include < direct.h > # include <errno.h > La funci6n mkdir( ) devuelve el valor 0 si se ejecuta satisfactoriamen- te y un valor -1 si ocurre un error. main( ) { if ( mkdir("c: word ayuda") -1 ) puts("Error: directorio no creado"); else puts("Directorio creado"); # include <direct.h > # include <errno.h >
  • 655. La funcion rmdir( ) devuelve el valor 0 si se ejecuta satisfactoriamente y un valor distinto de 0 si ocurre un error. main( ) { if ( rmdir(Hc: word ayuda") = = -1 ) puts(HError: directorio no borrado"); else puts(HDirectorio borrado"); Esta fund on almacena en la variable buffer el camino (path) correspon- diente al directorio detrabajo actual. EI argumento n especifica la longi- tud maxima para el nombre del camino. Si la longitud maxima de este es superior a n se obtiene un error. # include <direct.h > # include <errno.h > La fundon getcwd( ) devuelve un puntero al camino (path) obtenido o un NULL para indicar que ha ocurrido un error. # include <stdio.h> # include <direct.h >
  • 656. main( ) [ char camino_actual[N], camino[N]; getcwd( camino_actual, N ); puts( '~ directorio ?" ); gets( camino ); chdir( camino ); system( "dir *.exe" ); chdir( camino_actual ); J Entrada: Salida: c: c bin ficheros .exe del directorio bin Cuando en un programa Cencontramos una Hamada a esta fund6n, se ejecuta la orden DOS espedficada por cadena y al finalizar la ejecuci6n se continua en la siguiente sentencia del programa. El argumento cadena puede ser un programa .EXE, .COM 0 .BAT.Si el valor de cadena es NULL la fund6n simplemente chequea si esta presente el interprete de 6rdenes COMMAND.COM. # include < direct.h > # include <process.h > # include <errno.h > La funci6n system () devuelve un valor distinto de cero si cadena vale NULL y COMMAND.COM existe y un valor 0 si COMMAND.COM no existe.
  • 657. Si cadena tiene un valor distinto de NULL la funci6n system( ) de- vuelve el valor 0 si se ejecuta satisfactoriamente y un valor -1 si ocurre un error. Cuando se ejecuta esta funci6n se carga y ejecuta una nueva copia de COMMAND.COM, por 10 que system ( ) no puede ser utilizada para mo- dificar las variables de entorno actuales. # include <stdio.h> # include <stdlib.h> #dejine N 81 main( ) ( FILE *pjichero; char bujjer[N]; system("cls"); / * Abrir el jichero alumnos para escribir */ if ((pjichero = jopen("alumnos': "w")) = = NULL) ( printj("EI jichero alumnos no puede abrirse. n"); exit(l); l printf("Pulse AZ para jinalizar n n"); printf("Alumno: "); while (fgets(bujjer, N, stdin) != NULL) ( jputs(bujjer, pjichero); printf(" nAlumno: "); l jclose(pjichero); / * cerrar el jichero */ / * Clasijicar el jichero */
  • 658. system(Hsort < alumnos > alum"); system(Hcopy alum alumnos"); system(Hdel alum "); / * Abrir el jichero alumnos para leer */ if ((pjichero = jopen(Halumnos': Hr")) = = NULL) ( printj(HEI jichero alumnos no puede abrirse. n"); exit(l); } / * Escribir el contenido del jichero */ system(Hcls"); while (fgets(bujjer, N, pjichero) != NULL) printj(H%s n': bujjer); jclose(pjichero); / * cerrar el jichero */ } Este ejemplo crea el fichero alumnos, 10 clasifica y envia el resultado al fichero alum, copia alum en alumnos y borra alum. Ahora se tiene el fichero alumnos clasificado, que se visualiza en pantalla. Esta linea da lugar a que se cree el fichero INDICE con el contenido clasificado del directorio. Esta funci6n determina si el fichero especificado por path existe y si puede ser accedido en el modo especificado. # include <io.h > # include <errno.h >
  • 659. La funci6n access( ) devuelve un valor 0 si el fichero tiene el modo especificado. Si el fichero no existe 0 si no es accesible en el modo especifi- cado, la funci6n devuelve un valor -1 y la variable errno es puesta al valor correspondiente. Esta funci6n cambia el permiso que tiene el fichero especificado por path por el especificado por pmodo. El significado de este argumento es el mis- mo que se ha expuesto en la fund6n open( ). #include <sys types.h > # include <sys stat.h > # include <io.h > # include <errno.h > La funci6n chmod( ) devuelve un valor 0 si el permiso es cambiado. Un valor -1 indica un error.
  • 660. 1************ Cambiando los atributos de un jichero ************1 1* CCHMOD.C *1 # include <stdio.h> # include <stdlib.h> # include <conio.h > # include <io.h > # include <sys types.h > # include <sys stat.h > # include <sys utime.h > main(int argc, char ~rgv[ J) [ 1* Chequear si el jichero existe *1 if (!EXISTE(argv[IJ)) [ printj("Pormato: CCHMOD nombre"); exit(l); J 1* Chequear si el jichero tiene permiso de escritura *1 if ( !access(argv[l], ESCRIBIR)) [ printj("Pichero %s. Atributos: LEERIESCRIBIR n': argv[IJ); printj('~ Cambiar atributos a LEER ? (sin) "); if (tolower(getche( )) = = 's') chmod(argv[l], S-.1READ); J else [ printj("Pichero %s. Atributos: LEER n': argv[IJ); printf("" Cambiar atributos a LEERIESCRIBIR ? (sin) "); if (tolower(getch()) = = 's') chmod(argv[l], S-.1READ I S-.1WRITE);
  • 661. printf(Ha fecha actual ? (sin) "); if (tolower(getche( )) = = <s') utime( argv[l], NULL ); Esta funci6n extiende 0 trunca a la longitud especificada por n el fichero cuyo numero asociado es num. El fichero debe ser abierto en un modo que permita escribir. Si e1fichero es extendido se afiaden caracteres nulos (' 0'). # include <io.h > # include <errno.h > La funci6n chsize() devuelve un 0 si el fichero es cambiado. Un valor -1 indica un error y la variable errno es puesta al valor correspondiente. int nf; nf = open(Hdatos': O-RDWR, S--1READ I S--1WRITE); # include <stdio.h > # include <io.h > # include <errno.h > La funci6n unlink( ) devuelve un 0 si el fichero es borrado. Un valor -1 indica un error y la variable errno es puesta al valor correspondiente.
  • 662. if (unlink("datos") = = -1) perror("No se puede borrar el jichero"); else printjt jichero borrado "); Esta fundon renombra el fichero 0 directorio especificado por pf_actual con el nombre especificado por pf_nuevo. Esta funcion puede tambien ser utilizada para mover un fichero a otro directorio especificado por pf_nuevo. Sin embargo, un fichero no puede ser movido de un dispositi- vo a otro, por ejemplo de la unidad A a la unidad B. # include < stdio.h > # include < io.h > # include <errno.h > La fundon rename( ) devuelve un 0 si el fichero es renombrado. Un valor distinto de 0 indica un error y la variable errno es puesta al valor correspondiente. Esta funcion asigna el modo texto (0_TEXT) 0 el modo binario (O_BINARY) al fichero cuyo numero asodado es nurn.
  • 663. # include <fcntl.h > # include <io.h > # include <errno.h > La funci6n setmode( ) devue1veel modo anterior. Un valor -1 indica un error y la variable errno es puesta al valor correspondiente. Esta funci6n normalmente es utilizada para modificar el modo por defecto (O_TEXT) de los ficheros estandar. Esta funci6n obtiene informaci6n acerca del fichero 0 directorio especifi- cado por path y la almacena en la estructura de tipo stat apuntada por buffer. #include <sys types.h > # include <sys stat.h > # include <errno.h > [ dev_t st_dev; ino_t st_ino; unsigned short st_mode; short st--fllink; short st_uid; short st---f5id; 1* caracterfsticas: directorio, fichero, permisos LIE etc. *1 1* siempre es 1 *1 I * solo para UNIX *1 I * solo para UNIX *1
  • 664. dev_t st_rdev; oif_J sL-size; time_t st_atime; time_t st_mtime; time_t st_ctime; ]; / * igual que st_dev */ / * tamafio del jichero en bytes */ / * ultima vez que se modifico */ / * igual que st_atime */ / * igual que st_atime */ La funci6n devuelve un 0 si no hay error. Un valor -1 indica un error y la variable errno es puesta al valor correspondiente. struct stat p; stat(Hdatos': &p); printftCModificado el: ': ctime(p.st_atime)); Esta funci6n determina si num esta asociado con un dispositivo (un termi- nal, consola, impresora 0 puerto serie). La funci6n isatty( ) devuelve un valor distinto de 0 si num correspon- de a un dispositivo tipo caracter y 0 en caso contrario. int n; n = isatty(fileno(stdout)); Esta funci6n cambia la fecha y hora en la que fue modificado por ultima vez el fichero especificado por path. El fichero tiene que tener acceso para
  • 665. escribir. La estructura t contiene la fecha del ultimo acceso y la fecha de la ultima vez que fue modificado el fichero. Bajo MS-DOS la fecha del ultimo acceso no tiene sentido, por 10 que este concepto es ignorado. Si t es NULL la modificaci6n se hace d(; acuerdo con la fecha y hora actuales. # include <sys types.h > # include <sys utime.h > # include <errno.h > La funci6n utime( ) devuelve un cero si la modificaci6n es hecha. Un valor -1 indica un error y la variable errno es puesta al valor correspondiente. if (utime(''lc/source/datos': NULL) -1) perror('jecha no modijicada"); else printf('jecha modijicada "); Cuando la funci6n main( ) retorna al DOS puede utilizarse una orden de proceso por lotes IF para verificar el c6digo de salida retornado. Por ejemplo: El resultado de la orden anterior sera verdad si el c6digo de salida es mayor 0 igual que 1 ( > = 1 ). El resultado de la orden anterior sera verdad si el c6digo de salida es menor que 1 ( < 1 ).
  • 666. El 80x86 soporta 256 interrupciones diferentes identificadas cada una de ellas por un numero entre OOHy FFH. Las direcciones segmentadas de las 256 rutinas de tratamiento de la interrupci6n estan almacenadas en una tabla de vectores de interrupci6n que comienza en la direcci6n OOOO:OOOOH. Cuando se da una interrupci6n el control del ordenador es pas ado a una subrutina de tratamiento de la interrupci6n, que a menudo esta almacena- da en la ROM del sistema, cuya direcci6n de comienzo viene dada por el vector de interrupci6n correspondiente. Los numeros de interrupciones del DOS van des de 20H a 3FH. Las siguientes funciones C manipulan los servicios proporcionados por el DOS a traves de la interrupci6n Ox21. En general, todas las funciones de la interrupci6n Ox21 se llaman ejecutando la interrupci6n Ox21 con un numero de funci6n en el registro AH. La mayoria de ellas devuelven un c6digo de finalizaci6n en los registros AL 0 AX. Las funciones prototipo que se exponen a continuaci6n se encuentran dec1aradas en el fichero dos.h. El control es pasado de una rutina de interrupci6n a otra (vect_int) como si esta otra rutina de interrupci6n hubiera side llamada originalmente. Asigna un bloque de memoria utilizando la funci6n Ox48 del DOS, t es el tamaiio en bloques de 16 bytes y seg es la direcci6n del segmento donde va a ser ubicado el bloque. La funci6n retorna un valor 0 si se ejecuta satis- factoriamente. Un valor distinto de 0 indica un error.
  • 667. Esta funci6n cierra el fichero que tenga como descriptor num utilizando la funci6n Ox3Edel DOS. La funci6n retorna un valor 0 si se ejecuta satis- factoriamente. Un valor distinto de 0 indica un error. Crea un fichero utilizando la funci6n Ox3C del DOS. Si el fichero existe se borra. jich es el nombre del fichero, atrib son los atributos dados al fi- chero (-A_NORMAL, -A_RDONLY, -A_HIDDEN, -A_SYS- TEM, -A.- VOLID, -A_SUBDIR, -A-ARCH) y num es un puntero a una variable que contiene el descriptor del fichero. Verla funci6n creat(). La funci6n retorna un valor 0 si se ejecuta satisfactoriamente. Un valor distinto de 0 indica un error. Crea un fichero utilizando la funci6n Ox5Bdel DOS. Si el fichero existe se obtiene un error. unsigned _dos_findfirst(const char *jich, unsigned atrib, struct find_t *injo-fich); Libera un bloque de memoria asignado por __dos_allocmen() utilizand la funci6n Ox49del DOS. La funci6n retorna un valor 0 si se ejecutasati factoriamente. Un valor distinto de 0 indica un error. Encuentra la primera entrada de directorio que concuerde con los datos especificados utilizando la funci6n Ox4E del DOS. injo-fich es una es- tructura de tipo find_t declarado en dos.h, que contiene informacion sa- bre el fichero. La funci6n retorna un valor 0 si se ejecuta satisfactoriamen· te. Un valor distinto de 0 indica un error. Encuentra la siguiente entrada de directorio que concuerde utilizandola funci6n Ox4F del DOS.
  • 668. Obtiene la fecha utilizando la funcion Ox2A del DOS, fecha es una estruc- tura de tipo dosdate_t declarado en dos.h, que contiene los datos relati- vos a la fecha. Obtiene la unidad de disco actual (A: = 0, B: Ox19 del DOS. Obtiene el espacio libre del disco utilizando la funcion Ox36 del DOS, drv es la unidad de disco y esp es un puntero a una estructura de tipo diskfree_t declarado en dos.h, que contiene los datos relativos al espacio en disco. La funcion retorna un valor 0 si se ejecuta satisfactoriamente. Un valor distinto de 0 indica un error. Obtiene los atributos actuales del fichero 0 directorio utilizando la fun- cion Ox43 del DOS. La funcion retorna un valor 0 si se ejecuta satisfacto- riamente. Un valor distinto de 0 indica un error. Obtiene la fecha y hora asociadas con el fichero utilizando la funcion Ox~7 del DOS. La funcion retorna un valor 0 si se ejecuta satisfactoriamente. Un valor distinto de 0 indica un error. Obtiene la hora utilizando la funcion Ox2C del DOS, hora es una estructu- ra de tipo dostime_t declarado en dos.h, que contiene los datos relativos a la hora. Obtiene el vector de interrupcion asociado con el numero de interrupcion especificado utilizando la funcion Ox35 del DOS.
  • 669. Instala un programa residente en memoria (TSR) utilizando la funcion Ox31 del DOS, cod es el codigo de salida y t es el numero de bloques de 16bytes necesarios para instalar el programa. El espacio en excesoes devuelto al DOS. Abre un fichero utilizando la funcion Ox3Ddel DOS. Verla funcion open( ). La funcion retorna un valor 0 si se ejecuta satisfactoriamente. Un valor distinto de 0 indica un error. unsigned _dos_read(int num, void _far *buffer, unsigned c, unsigned *b); Lee datos de un fichero utilizando la funcion Ox3F del DOS. Ver la fun- cion read( ). num es el descriptor del fichero, buffer almacena los bytes leidos, c es el numero de bytes a leer y b es el numero de bytes actualmente !eidos. La funci6n retorna un valor 0 si se ejecuta satisfactoriamente. Un valor distinto de 0 indica un error. Cambia el tamafio de un bloque de memoria, previamente asignado por _dos_allocmen, utilizando la funcion Ox4Adel DOS, t es el nuevo tam a- fio, seg es direccion del segmento del bloque de memoria y tm es un punte- ro a un entero que indica el tamafio maximo que puede ser asignado. La funcion retorna un valor 0 si se ejecuta satisfactoriamente. Un valor distin- to de 0 indica un error. Establece la fecha utilizando la funcion Ox2Bdel DOS, fecha es una es- tructura de tipo dosdate_t declarado en dos.h, que contiene los datos re- lativos a la fecha. Selecciona la unidad de disco utilizando la funcion OxOEdel DOS, drv es O=unidad actual, 1=A:, 2=B:, etc.; num_drv es un puntero a un entero que indica el numero total de unidades.
  • 670. unsigned _dos_setfiJeattr(const char *path, unsigned atrib); :~tablece los atrib.~tos del fichero 0 directorio utilizando la funcion Ox43 U e DOl S.d~a .funclOn retorna un valor 0 si se ejecuta satisfactoriamente n vaor lstmto de 0 indica un error. . unsigned _dos-setftime(int num, unsigned fecha, unsigned hora); Establece la fecha y hora asociadas con el fichero utilizando la funcion Ox57 del DOS. La funcion retorna un valor 0 si se ejecuta satisfactoriamente. Un valor distinto de 0 indica un error. Establece la hora utilizando la funcion Ox2Ddel DOS. hora es una estruc- tura de tipo dostime_t declarado en dos.h, que contiene los datos relati- vas a la hora. void _dos_setvect(unsigned num_int, void ( ~nterrupt _far *pint)( »; Establece el vector de interrupcion (pint) para un numero de interrupcion (num_int) especificado, utilizando la funcion Ox25 del DOS. unsigned _dos_write(int num, void _far *buffer, unsigned c, unsigned *b); Escribe datos en un fichero utilizando la funcion Ox40del DOS. Ver la fun- cion writer ). num es el descriptor del fichero, buffer contiene los bytes a escribir, c es el numero de bytes a escribir y b es un puntero al numero de bytes actualmente escritos. La funcion retorna un valor 0 si se ejecuta sa- tisfactoriamente. Un valor distinto de 0 indica un error. Obtiene informacion extendida de un error utilizando la funcion Ox59del DOS. info_error es un puntero a una estructura de tipo DOSERROR de- clarado en dos.h, que contiene informacion relativa al error ocurrido.
  • 671. Sustituye una rutina de tratamiento de un "error critico" de la interrup- ci6n Ox24 hecha a medida (pint) por la que proporciona el DOS. El retor- no desde esta rutina puede producirse desde una sentencia return, desde una funci6n Jardresume( ) 0 desde una funci6n _hardretn( ). Retorna al DOS despues de un error de hardware. resu puede tener uno de los siguientes valores: _HARDERR_IGNORE, _HARDERR_RE- TRY, _HARDERR~BORT, _HARDERR_FAIL. Retorna al programa despues de un error de hardware. error es un c6digo de error de DOS. Segun hemos visto la funci6n Ox31 de la interrupci6n Ox21 (0 su eqpivalen- te INT Ox27) proporcionan el mecanismo para dejar residente un progra- ma en memoria (TSR). Normalmente un programa residente consta de dos partes: la parte residente que queda en memoria y la parte transitoria que instala e inicializa la parte residente y no queda en memoria. Escribir la hora en la esquina superior izquierda de la pantalla utili- zando un programa residente. Esto nos permitini ejecutar simultaneamen- te cualquier otro proceso. Para realizar este ejemplo utilizaremos ademas de la funci6n Ox31, la interrupci6n OxIC ("tic" del temporizador). La interrupci6n de software OxIC es invocada por la interrupci6n Ox08 cada vez que esta se produce.
  • 672. El temporizador del sistema emite la interrupcion Ox08 cada 1/18.2 segun- dos con el fin de actualizar el contador de tiempos. Por ello, la interrup- cion Ox08 recibe el nombre de "tic". Segun 10 expuesto, una rutina del usuario apuntada por el vector de interrupcion correspondiente al numero de interrupcion OxIC se ejecutara por cad a "tic" del contador de tiempos. Siguiendo el mecanisme descrito, el programa correspondiente al pro- blema planteado, es el siguiente: # include <dos.h > # include <time.h > # define NUM~NT OxlC void (_cdecl ~nterrupt _far *pint)(void); void _cdecl ~nterrupt _far mi_int(void); _segment segvar = OxB800; / * Para mono sustituir por OxBOOO*/ int i, _based(void) *PV; char hora[9]; int atributo = Ox7000; unsigned long tics; / * hora del sistema: hh:mm:ss 0 */ / * video inverso: byte alto = Ox70 */ int main(void) { / * Salvar el vector de interrupci6n de la INT OxlC */ pint = _dos~etvect(NUM~NT); / * Hora inicial; hora = hh:mm:ss 0 */ ---.Strtime(hora); / * Poner en la interrupci6n Oxle, * el vector de interrupciones mi_int */ _dos---.Setvect(NUM~NT, mi_int);
  • 673. 1* Visualizarlguardar la hora inicial en vkieo inverso *1 for (i= 0, pv = (int _based(void) *)144;i <8; i+ +, pv + +) { carli] = hora[i] I atributo; *(segvar:>pv) = car[i]; 1* Instalar nuestra rutina de interrupci6n (TSR) *1 _dos_keep(O, 256); 1* (4K*1024)/16 = 256 pdrrajos *1 1* usar la utilidad EXEHDR d void _cdecl ~nterrupt _far mi_int(void) { tics+ +; h 1 segundo = 18.2tics *1 if (!(tics % 18L)) ( _enable( ); 1* Actualizar la hora *1 -strtime(hora); 1* Actualizar los segundos *1 car[7] hora[7] I atributo; car[6] = hora[6] I atributo; 1* Actualizar los minutos *1 if (hora[6] = = '0') { car[4] = hora[4] I atributo; car[3] = hora[3] I atributo; if (hora[3] = = '0') { 1* Actualizar las horas *1 car[l] = hora[l] I atributo; carrO] = hora[O] I atributo; J J
  • 674. / * Visualizar la hora */ for (i= 0, pv = (int _based(void) *)144; i<8; i+ +, pv + +) *(segvar:>pv) = ear[i]; } / * Pasar el control a la [NT Ox1C */ _ehain~intr(pint); } La fund6n _dos_keep( ) asegura que la rutina de interrupci6n mi_int( ) permanezca instalada en memoria hasta que el vector de inte- rrupci6n correspondiente a la INT OxIC sea reemplazado 0 hasta que se reinicialice e1sistema. Mientras mi_int no es invocada el cursor queda libre (no se esta es- cribiendo el re1oj), 10 que permite disponer del prompt del DOS y ejecutar cualquier otro proceso.
  • 675. Cada program a que es ejecutado por el sistema operativo debe ser primero transformado en un proceso. Un proceso es una unidad ejecutable, a la que el sistema operativo Ie ha asignado recursos tales como memoria princi- pal, memoria secundaria, dispositivos perifericos y UCP. Con el DOS, los conceptos de programa y proceso son intercambia- bles, porque el DOS es un sistema operativo monotarea; esto significa que s610 es posible ejecutar un proceso cada vez. Esta circunstancia no se da en un sistema operativo multitarea como OS/2 0 como UNIX. En el DOS, la iniciaci6n, ejecuci6n y finalizaci6n de un proceso es res- ponsabilidad del interprete de 6rdenes COMMAND.COM. Cuando esto ocurre asi COMMAND.COM actua como proceso padre y el proceso ini- ciado recibe el nombre de proceso hijo. Un proceso hijo puede a su vez actuar como padre e iniciar otro proceso hijo. Las funciones que vamos a exponer en este capitulo tienen como finalidad la iniciaci6n, ejecuci6n y finalizaci6n de un proceso.
  • 676. La iniciaci6n de un proceso bajo DOS, lleva consigo la ejecuci6n de los siguientes pasos: 1. Asignaci6n del espacio en memoria necesario para cargar el pro- ceso. Un espacio insuficiente hace que el programa no sea ejecu- tado. La distribuci6n de este espacio es de la forma siguiente: 2. Se construye el prefijo del segmento del programa (PSP). Este tiene un tamafio de 256 bytes y no tiene nada que ver con la cabecera del fichero almacenado en el disco. El DOS coloca en esta zona una informaci6n indispensable, entre la que cabe destacar: • El c6digo de instrucci6n de INT Ox20 que provoca el retorno al programa llamador (generalmente este programa es COM- MAND.COM). Esta interrupci6n restaurani los vectores de in- terrupci6n Ox22, Ox23 y Ox24 con el valor original que tenian antes de comenzar la ejecuci6n del proceso. • Primera direcci6n segmentada por encima de la memoria asig- nada al programa. Un programa puede utilizar esta direcci6n para determinar el tamafio real de la memoria que tiene asignada. • Llamada al planificador de funciones del DOS. Su funci6n es ejecutar la rutina de servicio correspondiente al numero de fun- ci6n que Ie es pasado.
  • 677. • La direcci6n a la que se pasani el control cuando la ejecuci6n del programa finalice con una llamada a la INT Ox20. Este va- lor se copia en el vector de interrupci6n Ox22. • La direcci6n a la que se pasani el control en caso de un Ctrl +C. Este valor se copia en el vector de interrupci6n Ox23. • La direcci6n a la que se pasani el control en caso de un "error fatal". Este valor se copia en el vector de interrupci6n Ox24. • 20 octetos que contienen los descriptores (0 a 19) para manejo de los ficheros abiertos. Los 5 primeros estan normalmente asig- nados y se corresponden con los dispositivos estandar de E/S. • Direcci6n del bloque que contiene las variables de entorno. En C esta informaci6n es gestionada por la funci6n getenv( ) y pu- tenv( ). • Parametros de la linea de 6rdenes tecleados por el usuario cuando invoc6 al proceso (128 bytes). En C argc y argv permiten pasar estos parametros a la funci6n main(). Este area sirve igualmente como area de transferencia del disco por defecto, que el DOS utilizara en la E/S de ficheros. El PSP da una entidad propia al proceso. Entre otras cosas asegura, indistintamente de que el proceso termine normalmente 0 no, que el DOS recupere el control sobre los recursos que han sido asignados a dicho proceso. Cuando un proceso comienza a ejecutarse pueden tomarse acciones que alteren su normal desarrollo. Tales acciones pueden ser las siguientes: 1. Saltos no locales. Cuando ocurre un saIto de una funci6n a otra esta claro que hay que salvar en la pila el estado de la funci6n ac- tual (setjmp( )). Esto permitira mas tarde restaurar (longjmp( )) el estado en el que se encontraba el sistema antes de efectuar el saIto.
  • 678. SIGABRT SIGFPE SIGINT Terminaci6n anormal Excepciones en coma flotante Ctrl +Break 0 Ctrl +C Las senales de proceso son provistas para manipular ciertas interrupciones comunes de una manera estandar (signa/( ) y raiser ). Para estas y otras senales pueden escribirse tambien ruti- nas no estandar. 3. Funciones ejecutadas antes de que el proceso devuelva el control. Durante la ejecuci6n de un proceso pueden ser invocadas hasta 32 funciones para ejecutarlas en el caso de que la ejecuci6n del proceso termine normalmente y antes de que este devuelva el con- trol (atexit( ) u onexit( ). 4. Iniciaci6n de procesos. Un proceso puede ser liberado para ini- ciar otro proceso utilizando las familias de funciones execxxx( ) y spawnxxx( ) 0 por la funci6n system( ). Cuando un proceso finaliza de una forma normal son ejecutadas las si- guientes tareas: 1. Se ejecutan en orden inverso (LIFO) las funciones invocadas por atexit( ) u onexit( ). 4. Se cierran todos los ficheros abiertos y se borran todos los fiche- ros temporales. 5. Se ejecutan todos los procesos de terminaci6n (coma flotante, res- taurar divisi6n por cero (INT OxOO),restaurar interrupciones so- lapadas, restaurar INT Ox22, Ox23 y Ox24).
  • 679. 6. Se termina el proceso (INT Ox21,fund6n Ox4C): se libera la me- moria asignada al proceso, excepto la memoria asignada dimimi- camente por e1propio proceso, y se retorna un c6digo de salida al proceso padre. De las funciones disponibles para terminar un proceso, s610exit() fi- naliza un proceso normalmente. Las fundones abort( ) y assert( ) s610 in- c1uyen el punta 5, terminando el proceso de una forma anormal. Cuando se ejecuta un programa desde el sistema operativo se inicia un pro- ceso. Utilizando las funciones de control de procesos se puede inidar, eje- cutar y parar un proceso, desde cualquier otro proceso. Esta fund6n ejecuta una terminaci6n no normal de un proceso. No des- carga los buffers de los ficheros y no ejecuta los procesos lanzados por ate- xit( ) u onexit( ). #include < stdlib.h > # include <process.h > La fund6n abort( ) escribe el mensaje "abnormal program termina- tion" y devuelve al DOS un c6digo 3. if ((pj = jopen(argv[argc-l], Ur")) = = NULL) abort( );
  • 680. Esta fundon finaliza de una forma normalla ejecudon de un proceso. Antes de dar por finalizado el proceso la fundon exit( ) ejecuta en orden inverso (LIFO) las funciones invocadas por atexit( ) u onexit( ), descarga los buf- fers, cierra todos los ficheros y retorna al proceso padre con un codigo de salida dado por estado. Esta funcion es preferible a cualquier otra que pueda realizar una fundon similar. # include < stdlib.h > # include < proeess.h > El argumento estado normalmente es 0 para indicar una salida nor- mal y otro valor para indicar un error. Esta fundon no retorna un valor. Esta funcion da lugar a que la fundon referendada por fune sea ejecutada cuando el programa termina normalmente 0 por via exit(). Pueden ser re- gistradas un maximo de 32 fundones para ser llamadas sucesivamente. Cuan- do existen varias llamadas consecutivas a la fundon atexit( ) se ejecutan desde la ultima a la prim era. Esta fundon devuelve un 0 si se ejecuta satisfactoriamente 0 un valor distinto de 0 si ocurre un error.
  • 681. # include <stdio.h> # include <stdlib.h> void fund (void); void fune2(void); main( ) ( atexit(fund); atexit(fune2); printj("Punci6n principal n "); } void fund (void) ( printj("Punci6n 1 n "); } void fune2(void) ( printj("Punci6n 2 n"); } Funci6n principal funci6n 2 funci6n 1 La funci6n onexit( ) actua igual que la funci6n atexit( ), pero a dife- rencia de esta, solamente puede utilizarse cuando fune es un puntero a una funci6n de tipo onexit_t.
  • 682. Es preferible utilizar la funcion atexit( ) ya que es una funcion C es- tandar. Una llamada a esta funcion salva en la variable eDt, el actual entorno de la pila (conjunto de valores que se guardarian en la pila, por ejemplo, cuando se llama a una funcion). Una llamada posterior a la funcion longjmp( ) restaura el entorno salvado y devuelve el control a la sentencia que esta a continuacion de setjmp( ). La funcion setjmp( ) devuelve un valor 0 despues de salvar el entorno de la pila; 0 bien, devuelve el argumento valor de longjmp() si a continua- cion de setjmp( ) se llamo a esta funcion (ver longjmp( ). Si el argumento valor de longjmp( ) es 0 la funcion setjmp( ) retorna un 1. Recordar que una sentencia de bifurcacion incondicional goto puede solamente transferir el control a una etiqueta dentro de la propia funcion. Por 10 tanto, para ejecutar un salta de una funcion a otra (no local) utilizar conjuntamente las funciones setjmp( ) y longjmp( ). Restaura el entorno (eDt) salvado por setjmp( ) y devuelve el control a la sentencia que esta a continuacion de setjmp( ). EI argumento valor es un valor distinto de 0 para devolverselo a setjmp( ).
  • 683. Los valores de las variables register de la rutina que llam6 a lafunci6n setjmp( ) no son restaurados despues de ejecutarse la funci6n longjmp(). EI resto de las variables conservan sus valores. Esta funci6n reinicializa el paquete matematico para operar en coma flo- tante. Normalmente se utiliza junto con las funciones signal( ), system ( ), exec( ) 0 spawn( ). Un programa que utiliza una rutina para manipular errores en coma flotante (sefial SIGFPE) tiene que recuperarse de un error invocando a -fpreset( ) y utilizar longjmp( ). # include <stdio.h> # include <signal.h > # include <setjmp.h> # include <jloat.h> # include <math.h > # include <string.h > jmp_buj marca; int err_cj; / * direcci6n para longjmp */ / * mimero de error */ void rutina~err _cj(int sig, int num); void chequear_err(void); int main(void) { double nl, n2, r; int reL.Jmp;
  • 684. / * Instalar la rutina para manipulaci6n de errores * en coma flotante. */ signal(SIGFPE, rutina_err _cf); / * Salvar el entorn0 de la pi/a para retornar en caso de error. * La primera vez, ret-Jmp es 0; se ejecuta if. Si ocurre un * error ret-Jmp serra puesto a -J y serra ejecutada la * rutina chequear_err. */ ret-Jmp = setjmp(marca); if (ret-Jmp = = 0) ( printj(HProbar operaciones invalidas. "); printj(Hlntroducir dos mimeros: "); scanft'%/j %/j': &nJ, &n2); / * Si en las operaciones siguientes ocurre un error, * se ejecuta la rutina chequear_err */ r = nJ / n2; printj(H n n%g / %g r = nJ * n2; printj(H n n%g * %g = %g n': nJ, n2, r); } else chequear_err( ); / * Manipulaci6n de fa interrupci6n SIGFPE. * Error en coma flotante. */ void rutina_err _cf(iot sig, iot num) ( err_cf = num; / *para evitar hacer E/S en la rutina */ / * Inicializar ef paquete de coma flotante */ -fpreset( );
  • 685. * a continuacion de setjmp. Devolver el valor -1 para que sea * falsa la condicion del if. */ longjmp(marca, -1); 1 void chequear_err( ) { char mensaje_err[30]; switch (err_cf) ( case FPE~NVALID: strcpy(mensaje_en; "Ntimero invdlido"); break; case FPE_OVERFLOW: strcpy(mensaje_err, "Overflow"); break; case FPE_UNDERFLOW: strcpy(mensaje_err, "Underflow"); break; case FPE.-Z,ERODIVIDE· strcpy(mensaje_err, "Division por cera"); break; default: strcpy(mensaje_err, "Error en coma flotante"); break; 1 printj("Error %d: %s n': err_cj, mensaje_err); 1 Esta funcion permite a un proceso escoger una de varias formas de mani- pular una serral de interrupcion. La fundon signal( ) indica que se ejecute
  • 686. la funci6n June cuando durante la ejecuci6n del proceso se de la interrup- ci6n sig. void (*signal(int sig, void(*!une) (int sig[, int subeod))))(int sig); EI argumento sig es una constante de las siguientes (definidas en signal.h): SIGINT 2 SIGFPE 8 SIGABRT 22 Interrupci6n Ctrl +C. Por defecto emite INT Ox23. Error en coma flotante. Termina el proceso. Terminaci6n anormal. Termina el proceso. C6digo de salida 3. La acci6n que se toma cuando se recibe la seftal de interrupci6n de- pende del valor de June. EI argumento June debe ser la direcci6n de una funci6n 0 una constante de las siguientes (definidas en signal.h): Respuesta por defecto del sistema (equivalente a la lla- mad a a la funci6n abort( ). Ignorar la seftal de interrupci6n. No utilizarla con SIGF- PE ya que el estado del proceso queda indefinido.
  • 687. Este ejemplo indica que si se produce un error en coma flotante, se ejecutani la funci6n del usuario mi---fune( ). Si el argumento June se corresponde con la direcci6n de una funci6n, entonces dicha funci6n se instala como rutina de manipulaci6n para la se- nal dada. Si la funci6n realiza un return, el proceso reanuda la ejecuci6n inmediatamente a continuaci6n del punta en el que se recibi6 la interrupci6n. Todas las funciones June de manipulaci6n de senales tienen un argu- mento (sig) y no devuelven un resultado (void), excepto SIGFPE que utili- za un argumento adicional, subeod, que identifica el tipo de error. Las cons- tantes validas (FPE-'CXX) para este argumento estan definidas en float.h. Por ejemplo: FPE---ZERODIVIDE, FPE_OVERFLOW, FPE_SQRT- NEG, etc. EI valor de June no es restaurado a su valor despues de recibir la se- nal. Para recuperarse de un error de coma flotante tenemos que utilizar conjuntamente las funciones setjmp() y longjmp(). Con respecto a SIGF- PE, si la funci6n instalada realiza un return, el proceso se reanudaria con un estado indefinido. Como las rutinas de manipulaci6n de senales son llamadas asincro- namente cuando ocurre una interrupci6n, es posible que nuestra funci6n de manipulaci6n coja el control cuando una operaci6n C se este ejecutan- do y aun no haya terminado (estado desconocido). Por ello, a este tipo de rutin as les aplicaremos las siguientes restricciones: 1. No incluir funciones de E/S de bajo nivel 0 funciones incluidas en stdio.h (por ejemplo printj( ), Jread( ), etc.). 2. No incluir funciones que utilicen directa 0 indirectamente la me- moria para asignaci6n dinamica (por ejemplo mal/oe(), strdup( ), etc.). 3. No incluir funciones C que generen llamadas al sistema (por ejem- plo getewd( ), timer ), etc.).
  • 688. 4. No utilizar la funci6n longjmp( ) a no ser que la interrupci6n sea originada por un error en coma flotante (per ejemplo el argumento sig es SIGFPE). Una selial puesta por signal() no es heredada por un proceso hijo creado por medio de exec( ) 0 spawn( ). Esta funci6n envia la selial sig al proceso activo (programa). Si anterior- mente se ha instalado una rutina de manipulaci6n (funci6n signal( )), en- tonces la funci6n raise() hace que se ejecute dicha rutina. En caso contra- rio se ejecutani la acci6n por defecto. La funci6n raiser) devuelve un 0 si se ejecuta satisfactoriamente 0 un valor distinto de 0 si no. # include <stdio.h > # include <conio.h > # include <signal.h> # include <stdlib.h > # include <dos.h > # include <bios.h > void manipular _Ctrl_C(void); void escribir(char ..str); iot leer(void);
  • 689. int main(void) { 1* Modificar rutina de interrupci6n CTRL +C *1 signal(SIGINT, manipular _Ctrl_C}; prinlf(<cVisualizar el c6digo de la(s) tecla(s) pulsada(s) n"); do ( car = getch( ); if (car = = 0) ( car = getch( ); if (car = = 46) 1* tratar ALT+C igual que CTRL+C *1 raise(SIGINT); else printj(<CC6digo extendido: %X n': car); } else printf(<CC6digo ASCIl- %X n': car); } while (car /= 27); } 1* Manipulaci6n de la interrupci6n SIGINT (CTRL +C) *1 void manipular _Ctrl_C( ) ( 1* Inhabilitar CTRL +C *1 signal(SIGINT, SIG~GN); 1* Evitar hacer EIS en la rutina *1 escribir(''(.Abortar el proceso? sin "); car = leer(); escribir(<C r n "); if (tolower(car) - - 's') abort( ); else
  • 690. I * La interrupci6n CTRL +C debe ser restaurada para nuestro * manipulador, ya que por deJecto serla restaurada al * manipulador del sistema. *1 signal(SIGINT, manipular _Ctrl_C); } 1* Escribe una cadena uti/izando //amadas al sistema *1 void escribir(char ~tr) [ union REGS inregs, outregs; inregs.h.ah = OxOe; while (~tr) [ inregs.h.al = ~tr+ +; int86(OxIO,&inregs, &outregs); } } I * Lee un cardcter uti/izando //amadas al sistema *1 int leer( ) [ return (_bios_keybrd(~EYBRD--.READ) & OxJf); } Esta fundon carga y ejecuta un nuevo proceso hijo. El proceso padre es reemplazado en memoria por el proceso hijo, el cual es ejecutado inmedia- tamente. int execl(path, argO,argl, argn, NULL); int execle(path, argO,argl, argn, NULL, envp); int execlp(path, argO,argl, argn, NULL);
  • 691. int execlpe(path, argO, argl, ... argn, NULL, envp); int execv(path, argv); int execve(path, argv, envp); int execvp(path, argv); int execvpe(path, argv, envp); char *path; char *argO,*argl,... *argn; char *argv[ ]; char *envp[ ]; nombre del proceso a ser ejecutado lista de punteros a argumentos array de punteros a argumentos array de punteros a variables del entorno del proceso. Existen varias versiones de execxxx( ), aunque todas ellas utilizan la misma funci6n exec( ). El sufijo xxx que se afiade indica que argumentos se pasan y c6mo se pasan. A continuaci6n se indican los sufijos y la varia- ci6n que estos producen. Los argumentos en la linea de 6rdenes son pasados a la fun- ci6n exec( ) individualmente. Se utiliza normalmente en los casos en los que el numero de argumentos es conocido. Los argumentos en la linea de 6rdenes son pasados ala fun- ci6n exec( ) como un array de punteros (...argv[ J). Se utiliza normalmente en los casos en los que el numero de argumen- tos es variable. Pasa explicitamente un array de punteros (*envp[ J) a las va- riables del entorno del proceso hijo.
  • 692. Cada elemento del array envp, excepto el elemento final, apun- ta a una cadena de la forma: donde VAR es el nombre de la variable y valor es la cadena de caracteres asociada. char *mi_en vp[ J { "VARl=abcde': "VAR2=xxx': NULL }; La funci6n exec() devuelve un valor -1 si ocurre un error y la variable errno es puesta al valor correspondiente. Los ficheros que estan abiertos cuando se ejecuta una Hamada a la funci6n exec( ) permanecen abiertos en e1nuevo proceso. La funci6n exec( ) no conserva el modo de traslaci6n de los ficheros abiertos; si es necesario utilizar la funci6n setmode( ). EI proceso hijo hereda el cntorno del proce- so padre. Este entorno puede ser modificado por medio de la variable envp. # include <stdio.h > # include <conio.h > # include <process.h> char *mi_ent[ J = { "UNO=variable 1':
  • 693. "DOS = variable 2': 'TRES=variable 3': NULL }; int main(void) ( char *'1rgs[4],prog[80]; int car; printj("Nombre del programa a ejecutar: "); gets(prog); printj(" n 1. execl printj(" 5. execv do { printf(" nEscribe un mimero entre 1 y 8 (0 para sa/ir): "); car = getche(); if (car- = = '0') exit(O); } while ((car < '0') II (car> '8')); printj(" n n "); 2. execle 6. execve 4. execlpe n' '); 8. execvpe n"); 3. execlp 7. execvp / * Argumentos para execvxx */ args[O] = prog; args[1] = "exec??"; args[2] = "dos"; args[3] = NULL; switch (car) ( case '1': execl(prog, prog, "execl': "dos': NULL); break; case '2': execle(prog, prog, "execle': "dos': NULL, mi_ent); break; case '3': execlp(prog, prog, "execlp': "dos': NULL); break;
  • 694. case '4': execlpe(prog, prog, Hexeclpe': Hdos': NULL, mi_ent); break; case '5': execv(prog, args); break; case '6': execve(prog, args, mi_entj; break; case 7': execvp(prog, args); break; case '8': execvpe(prog, args, mi_ent); break; l printf(H nProceso no ejecutado. n "); exit(l); J Esta funci6n crea y ejecuta un nuevo proceso hijo. La diferencia con exec() viene dada par el argumento modo; este determina la acci6n que toma el proceso padre, antes de y durante la ejecuci6n del proceso hijo. Los sufijos expuestos para exec( ) son v<ilidos tambien para spawn( ). int spawnl(modo, path, argO, argl, argn, NULL); int spawnle(modo, path, argO,argl, argn, NULL, envp); int spawnlp(modo, path, argO,argl, argn, "NULL); int spawnlpe(modo, path, argO,argl, argn, NULL, envp); int spawnv(modo, path, argv); int spawnve(modo, path, argv, envp); int spawnvp(modo, path, argv); int spawnvpe(modo, path, argv. envp);
  • 695. iot modo; char *path~· char *argO,*argl,... *argn; char *argv[ ]; char *envp[ ]; accion tomada por el proceso padre nombre del proceso a ser ejecutado lista de punteros a argumentos array de punteros a argumentos array de punteros a variables del entorno del proceso. Tiene el mismo efecto que las llamadas efectuactas con la funcion exec( ) (destruye el proceso padre). Suspende el proceso padre hasta que finalice la eje- cucion del proceso hijo (spawn sincrono). Continua la ejecucion del proceso padre concurren- temente con el proceso hijo (spawn asincrono; va- lido solamente en modo protegido). Continua la ejecucion del proceso padre e ignora las llamadas a wait( ) 0 cwait( ) contra los proce- sos hijo (spawn asincrono; valida solamente en modo protegido). Continua la ejecucion del proceso padre concurren- temente con el proceso hijo. El proceso hijo se des- liga de la jerarquia de procesos padre; si el proceso padre termina, el proceso hijo continua ejecutan- dose (spawn asincrono; valida solamente en modo protegido) . El valor devuelto por la funcion spawn ( ) sincrona (P_ WAIT) es el estado de salida del proceso hijo. EI valor devuelto por la funcion spawn( ) asincrona (P_NOWAIT, P~OWAITO) es el PID (identificacion del proceso). Para obtener el co-
  • 696. digo de salida para un proceso Hamado con el modo P_NOWAIT se debe Hamar a la funci6n wait( ) 0 cwait( ) y especificar ID. El c6digo de salida para un proceso Hamado con el modo P_NOWAITO no puede obtenerse. Si el proceso termina normalmente el c6digo de salida es O. Un valor positivo indica una terminaci6n anormal. Si ocurre un error el valor de- vuelto es -1 y la variable errno es puesta al valor correspondiente (el proce- so hijo no comienza). El c6digo de salida puede ser modificado si el proce- so hijo invoca explicitamente a la funci6n exit( ) con un valor diferente. Los ficheros que estan abiertos cuando se ejecuta una Hamada a la funci6n spawn(), permanecen abiertos en el nuevo proceso. El proceso hijo hereda el entorno del proceso padre. Este entorno puede ser modificado por medio de la variable envp. La funci6n spawn( ) pasa al proceso hijo informaci6n de los ficherQs abiertos, incluyendo el modo de traslaci6n, por medio de la variable de en- torno C---.FILE~NFO (_C---.FILE~NFO en modo protegido). Esta va- riable es utilizada por el c6digo de arranque de C (crtO.asm) para actuali- zar el PSP del proceso hijo antes de que la funci6n main( ) asuma el control. La variable -fileinjo determina si la informaci6n _C---.FILE~NFO es o no pasada. Si vale 0, esta informaci6n no es pasada y si vale 1 sf es pasa- da. La variable -fileinjo est a definida en stdlib.h y por defecto vale O. Si se desea que tenga el valor 1 hay que definirla explicitamente en el progra- ma C. Para cualquier stream previa a una Hamada a una funci6n spawn( ), debe ejecutarse explicitamente jjlush( ), jjlushall( ) 0 closer ). # include <stdio.h > # include < conio.h > # include <process.h> char *mi_en t[ J = { HUNO=variable 1': HDOS=variable 2':
  • 697. <TRES=variable 3': NULL }; int main(void) ( char *'lrgs[4],prog[80]; int car, r; printj(HNombre del programa a ejecutar: "); gets(prog); printj(H n 1. spawnl 2. spawnle 3. spawnlp 4. spawnlpe n "); printj(H 5. spawnv 6. spawnve 7. spawnvp 8. spawnvpe n"); do { printj(H nEscribe un mimero entre 1 y 8 (0 para saUr): "); car = getche( ); if (car = = <0')exit(O); } while ((car < <0') II (car > <8')); printj(H n n "); / * Argumentos para spawnvxx */ args[O] = prog; args[l] = "spawn??"; args[2] = Hdos"; args[3] = NULL; switch (car) ( case <1': r=spawnl(p _WAIT, prog, prog, "spawn/': "dos': NULL); break; case <2': r=spawnle(p _ WAIT,prog,prog, "spawnle': "dos':NULL,mi_ent); break; case <3': r=spawnlp(p _WAIT, prog, prog, "spawnlp': "dos': NULL); break;
  • 698. case '4': r=spawn/pe(p _ WAIT,prog,prog,"spawnlpe':' 'dos' :NULL,mi_ent); break; case '5': r=spawnv(p _ WAIT, prog, args); break; case '6': r=spawnve(p _WAIT, prog, args, mi_ent); break; case 7': r=spawnvp(p _ WAIT, prog, args); break; case '8': r=spawnvpe(p _WAIT, prog, args, mi_ent); break; 1 if (r = = -1) printj(H nProceso no ejecutado. n"); exit(r); Esta funci6n devuelve un puntero a la entrada de la tabla que contie- ne la variable 0 un valor NULL si la variable no esta definida. Esta funci6n anade, borra 0 modifica una variable de la lista de variables del entorno.
  • 699. Esta fundon devuelve un 0 si se ejecuta satisfactoriamente 0 un -1 si ocurre un error.
  • 700. PARTE 6 Graficos • Gnificos con C • Representaciones Gnificas
  • 701. Para poder ejecutar los ejemplos gnificos mostrados en este capitulo es ne- cesario tener un ordenador personal con una tarjeta gnifica CGA (Color Graphics Adapter), EGA (Enhanced Graphics Adapter), VGA (Video Grap- hics Adapter) 0 HGC (Hercules Graphics Card), entre otras. Tambien se necesita un monitor, monocromo 0 color, que soporte gnificos basados en puntos (pixels). El monitor del ordenador personal consta generalmente de 25 lineas de 80 caracteres, dependiendo estos datos de la interface de video instala- da y del modo de video seleccionado. La linea superior es la fila 1, y la posicion mas a la izquierda dentro de una fila, es la columna 1.
  • 702. La unidad elemental en un dibujo no es una celda (posici6n que ocu- pa un canicter), sino un punta en la pantalla. El numero exacto de puntos sobre la pantalla depende del hardware que se tiene instalado y de la mo- dalidad de video seleccionada (funci6n -.Setvideomode( ). Las coordena- das de la esquina superior izquierda de la pantalla son (0,0). Por ejemplo, el modo de video _ VRES16COLOR (VGA 16 colores), tiene una resolu- ci6n de 640 x 480, 10 que significa que el eje x contiene los valores 0 a 639 y e1 eje y contiene los valores 0 a 479 (ver figura). A la hora de desarrollar un programa grafico se deben tener en cuenta los siguientes cinco puntos: 5. Volver a la configuraci6n de video inicial antes de salir del programa.
  • 703. # include <stdio.h> # include <conio.h > # include <graph.h> int ModalidadDeVideo(void); struct videoconfig cv; / *funci6n prototipo */ / * datos referentes a la configuraci6n */ main( ) { / * Seleccionar la modalidad de video */ if (!ModalidadDeVideo( )) { printf(H%s n': Hmodalidad de video no soportada"); exit(O); l / * Determinar los parametros de la configuraci6n de video * seleccionada y almacenarlos en cv. */ ~etvideoconfig(&cv ); xm (cv.numxpixels/2-I); ym = (cv.numypixels/2-I); / * centro eje x */ / * centro eje y d _rectangle( _GBORDER, xm-80, ym-50, xm+80, ym+50); _ellipse( _GFILLINTERIOR, xm-70, ym-40, xm + 70, ym +40); / * Pulsar una tecla para continuar */ getch( ); / * Restaurar la configuraci6n inicial para salir * del program a
  • 704. */ ----setvideomode( ----.DEFAULTMO DE); 1 / * Sefeccionar fa modalidad de video */ int ModalidadDeVideo(void) { if (----Setvideomode(~ERCMONO)) return (~ERCMONO); if (----setvideomode(_ VRES16COLOR)) return (_ VRES16COLOR); if (----setvideomode(--.ERESCOLOR)) return(--.ERESCOLOR); if (----setvideomode(-MRES4COLOR)) return (-MRES4COLOR); else return (0); Este programa dibuja un rectangulo y una elipse inscrita en el rec- tangulo. Las constantes listadas a continuaci6n son utilizadas para activar la moda- lidad de video. La modalidad de video elegida debe ser compatible can el hardware instalado en la maquina. Constante Caracteristicas Modo Adaptador _DEFAULTMODE volver al modo original ambos todos _TEXTBW40 40 cols. texto, 16 grises texto CGA _TEXTC40 40 cols. texto, 16/8 colores texto CGA _TEXTBW80 80 coIs. texto, 16 grises texto CGA _TEXTC80 80 cols. texto, 16/8 colores texto CGA ~RES4COLOR 320 x 200 pixels, 4 colores gnificos todos _MRESNOCOLOR 320 x 200 pixels, 4 grises gnificos CGA _HRESBW 640 x 200 pixels, BW gnificos CGA
  • 705. Constante Caracteristicas Modo Adaptador _TEXTMONO 80 eols. texto, BW texto MDPA _HERCMONO 720 x 348 pixels, BW para HGC gnifieos HGC _MRESI6COLOR 320 x 200 pixels, 16 eolores gnifieos EGA _HRESI6COLOR 640 x 200 pixels, 16 eolores grafieos EGA _ERESNOCOLOR 640 x 350 pixels, BW gnifieos EGA _ERESCOLOR 640 x 350 pixels, 4 0 16 eolores gnificos EGA _ VRES2COLOR 640 x 480 pixels, BW gnifieos VGA _ VRES16COLOR 640 x 480 pixels, 16 eolores gnifieos VGA _MRES256COLOR 320 x 200 pixels, 256 eolores grafieos VGA _ORESCOLOR 640 x 400 pixels, 1 0 16 eolores (Olivetti) grafs. Para utilizar el modo _HERCMONO hay que instalar previamente el programa residente MSHERC.COM, suministrado con el paquete Mi- crosoft C. Yaque algunos adaptadores gnificos soportan varios modos de video, Microsoft C provee dos modos mas: Selecciona el modo de video de mas alta resolu- ci6n de entre todos los soportados por nuestro hardware. _MAXCOLORMODE Selecciona el modo de video que soporte mas co- lores de entre todos los soportados por nuestro hardware. Para seleccionar una modalidad de video de las anteriores, disponemos de la funci6n:
  • 706. Antes de salir del programa gnifico, debemos restaurar la modalidad de video original con la funci6n: Esta funci6n almacena valores y series de caracteres con formato en una memoria intermedia (buffer). iot spriotf(bujjer, jormato[, arg}...); char *bujjer; coost char *jormato; Los argumentos jormato y arg tienen el mismo significado que en la funci6n printj( ). La funci6n sprintj( ) devuelve el numero de caracteres almacenados en bujjer, sin incluir el canicter de terminaci6n O. iot c; char bujjer[80}; main( ) ( char *n = "Francisco Javier"; char *0 =.."Ceballos"; c = sprintj(bujjer, "Nombre: %s': n); c + = sprintj(bujjer+c, " %s n': a); printj("%s': bujjer); 1
  • 707. En este ejemplo c toma el valor del numero de caracteres actualmente almacenados en buffer. La expresi6n buffer+c incrementa el puntero buf- fer con el fin de afiadir la siguiente informaci6n a continuaci6n de la actual. Los panimetros de la configuraci6n seleccionada se almacenan en una es- tructura de tipo videoconfig, con el fin de utilizarlos con otras funciones graficas y para asegurar la portabilidad a otras configuraciones (CGA, EGA o VGA). Esta operaci6n se realiza mediante la funci6n -f5etvideoconfig( ). struct videoconfig cv; -f5etv ideoconjig( &cv); y = cv.numxpixe/s / 2 - 1; / * centro eje x */ x cv.numypixe/s / 2 - 1; / * centro eje y */ EI siguiente programa puede ser de gran utilidad, ya que presenta en pantalla las configuraciones de video que acepta nuestro ordenador. Cad a vez que se presente una configuraci6n pulse una tecla para ver la siguiente. # include <conio.h > # include <stdio.h> # include <graph.h> short modos[ ] = ! _TEXTBW40, _TEXTC40, _TEXTBW80, _TEXTC80, --.MRES4COLOR, --.MRESNOCOLOR, JlRESBW, _TEXTMONO, JlERCMONO, --.MRES/6COLOR,JlRES/6COLOR, JRESNOCOLOR, JRESCOWR, _VRES2COLOR, _VRES/6COLOR, --.MRES256COLO~ORESCOLOR };
  • 708. char *nombres[} = [ "TEXTBW40'; "TEXTC40'; "TEXTBW80'; "TEXTC80'; "MRES4COLOR'; "MRESNOCOLOR'; "HRESBW'; "TEXTMONO'; "HERCMONO'; "MRES16COLOR'; "HRES16COLOR'; "ERESNOCOLOR'; "ERESCOLOR'; "VRES2COLOR'; "VRES16COLOR'; "MRES256COLOR'; "ORESCOLOR" ]; / * Posible ntimero de filas */ short filas[ } = [ 60, 50, 43, 30, 25 ]; main( ) [ short c, i, j, x, y, nfilas; short num = sizeof(modos) / sizeof(modos[O}); struct videoconfig cv; / * conjiguraci6n de video */ char b[500}; / * buffer para la funci6n sprint! */ / * Poner a prueba cada modo */ for (i = 0; i < = num; i+ +) { for (j = 0; j < 5; j + +) [ / * Probar cada ntimero posible de filas */ nfilas = ---.Setvideomoderows(modos[i], filas[j}); if ((/nfilas) II (filas[j} /= nfilas)) continue; else [ / * obtener la configuraci6n de cv */ ~etvideoconfig( &cv); y = (cv.numtextrows - 14) / 2; x = (cv.numtextcols - 31) / 2; / * Elegir una ventana para sacar el texto */ ---.Settextwindow(y, x, cv.numtextrows-y, cv.numtextcols-x);
  • 709. CAPITULO 21: GRAFICOS CON C 733 * para despues visualizar el contenido del buffer */ c = sprintj(b, "Modalidad de video: %sn': nombres[iJ); c += sprintj(b + c, "Puntos eje X' O/Odn': cv.numxpixels}; c += sprintj(b + c, "Puntos eje }; O/Odn': cv.numypixels}; c += sprintf(b + c, "Columnas de texto: O/Odn': cv.numtextcols}; c += sprintj(b + c, "Fi/as de texto: O/Odn': cv.numtextrows}; c += sprintj(b + c, "Colores: O/Odn': cv.numcolors}; c + = sprintj(b + c, "Bits/pun to: %d n': cv.bitsperpixel}; c += sprintj(b + c, "Pdginas de video %d n': cv.numvideopages}; c += sprintj(b + c, "Modo: O/Odn': cv.mode}; c += sprintf(b + c, ''Adaptador: O/Odn': cv.adapter}; c += sprintf(b + c, "Monitor: O/Odn': cv.monitor}; c += sprintf(b + c, "Memoria: O/Od': cv.memory}; c + = sprintj(b + c, " n nPulse una tecla para continuar"}; / * Visualizar el contenido del buffer */ _outtext(b}; getch( }; / *pulsar una tecla para continuar */ J J J _displaycursor( _GCURSORON}; ---.Setvideomode(----.DEFAULTMO DE); J / * cursor visible d / * volver al modo inicial */
  • 710. Existen dos modalidades de video para texto en color: _ TEXTC40 y _ TEXTC80, las cuales pueden ser utilizadas con los adaptadores CGA, MCGA, EGA y VGA. Estos modos proporcionan 16 colores para el pri- mer plano y 8 colores de fondo. Cada canlcter requiere dos bytes de la memoria de video. El primero contiene el c6digo ASCII del canicter y el segundo los atributos de presen- taci6n. La tabla siguiente muestra los colores para el texto y sus valores aso- ciados (color del primer plano): Nro. Color Constante Nro. Color Constante 0 Negro _BLACK 8 Gris _GRAY 1 Azul _BLUE 9 Azul claro _LIGHTBLUE 2 Verde GREEN 10 Verde claro _LIGHTGREEN 3 Cyan CYAN 11 Cyan claro _LIGHTCYAN 4 Raja RED 12 Raja claro LIGHTRED 5 Magenta _MAGENTA 13 Magenta claro _LIGHTMAGENTA 6 Marron BROWN 14 Amarillo _LIGHTYELLOW 7 Blanco WHITE 15 Blanco intenso _BRIGHTWHITE Para el fondo es valida cualquier color del 0 al 7. Para seleccionar el color de fonda disponemos de la funci6n: Para escribir un texto en un color determinado, seleccionar primero el color ( --..Settextcolor(constante) ) y a continuaci6n visualizar el texto ( _outtext(buffer) ).
  • 711. # include <stdio.h> # include <graph.h > struct videoconfig cv; char *nombre[ } = ("NEGRO'; "AZUL'; "VERDE'; "CYAN'; "ROJO'; "MAGENTA'; "MARRON'; "BLANCO" J; main( ) { short cjondo; short ctexto; char buffer[80}; / * color de jondo de 0 a 7 */ / * color del texto 0 a 15 d / * almacenar cada linea a visualizar d -setvideomode(_TEXTC80); / * establecer la modalidad de video */ -ftetvideoconjig(&cv); / * conjiguraci6n de video */ for (cjondo = 0; cfondo < 8; cjondo+ +) / * color de jondo */ ( -setbkcolor(cjondo); -settextposition(l, 1); print/("Color de jondo: %7s n'; nombre[cjondo]); for (ctexto = 0; ctexto < = 16; ctexto + +) / * color texto */ ( -settextcolor( ctexto); -settextposition(5 +ctexto, 35); / * posicionar el cursor */ sprintf(bujjer, "Color: %2d n'; ctexto); _outtext(bujjer); / * visualizar el texto coloreado d } / *pulse una tecla para continuar */ getch( ); } _clear screen(_GCLEARSCREEN); -setvideomode( -DEFAULTMODE); } / * limpiar la pantalla */ / * volver al modo original */ Este programa presenta para cada color de fonda (0 a 7) los 16 colores posibles en los que se puede presentar un texto.
  • 712. Si al valor del color del primer plano Ie sumamos 16, se obtienen ca- racteres parpadeando; esto es, los valores 16 a 31 son los mismos colores o a 15, pero parpadeando. blanco sobre negro blanco intenso sobre negro blanco intermitente sobre negro blanco intenso e intermitente sobre negro negro sobre blanco amarillo sobre azul El color del primer plano puede coincidir con el color de fondo, 10 cual hace invisible cualquier canicter. Existen dos modalidades de video para gnificos en color utilizando un adap- tador CGA: ~RES4COWR y ~RESNOCOWR. Con CGA en alta resolucion,_HRESBW, solo es posible trabajar en blanco y negro. En modo gnifico un pixel es representado por un bit (blanco y negro), dos bits (4 colores), cuatro bits (16 colores), u ocho bits (256 colores), de- pendiendo del modo seleccionado. Con la modalidad de video ~RES4COWR, se dispone de cuatro paletas de cuatro colores cada una. Cada color tiene asociado un valor or- dinal de 0 al 3. El color 0 es el color de fondo, 10 que producini una salida invisible y los colores dell al 3, son tres colores de los 16 posibles.
  • 713. EI color de fondo puede tomar cualquier valor de 0 a 15y para selec- cionarlo disponemos de la funci6n: Verde Rojo Marr6n Cyan Magenta Oris claro Verde claro Rojo claro Amarillo Cyan claro Magenta claro Blanco Utilizando la modalidad de video ~RESNOCOLOR con un moni- tor blanco y negro se producen distintas tonalidades de grises. Si se utiliza con un monitor de color, se dispone de las dos paletas siguientes: Azul Rojo Oris claro Azul claro Rojo claro Blanco # include <stdio.h> # include <conio.h > # include <graph.h >
  • 714. long color-fondo[8] = (-BLACK, -BLUE, _GREEN, _CYAN, ---.RED,---.MAGENTA,-BROWN, _WHITE}; char *nombre[ ] = ("NEGRO': "AZUL': "VERDE': "CYAN': "Raja': "MAGENTA': "MARRON'; "BLANCO"}; main( ) ( int cfondo; int paleta; int color; / * color de fondo de 0 a 7 */ /* paleta de"O a 3 */ / * color 0 a 3 de la paleta elegida */ ---.Setvideomode(--.MRES4COLOR); / * modalidad de video */ ---ftetvideoconfig(&cv); / * configuraci6n de video */ for (cfondo = 0; cfondo < 8; cfondo+ +) /* color de fondo */ ( ---.Setbkcolor(color-fondo[cfondo]); for (paleta = 0; paleta < 4; paleta+ +) / * paleta elegida */ ( ---.Selectpalette(paleta); for (color = 0; color < 4; color+ +) / *primer plano */ ( ---.Settextposition(l, 1); ---.Setcolor(color); printj("Color de fondo: % 7s n': nombre[cfondo]); printj("Paleta: %16d nColor: %17d n': paleta, color); ---fectangle(_GFILLINTERIOR, 160, 100, 320, 200); / * pulse una tecta para continuar */ getch( ); } } } ---.Setvideomode(-DEFAULTMODE); } Este programa presenta todas las combinaciones de colores de fonda con las pal etas 0 a 3.
  • 715. Los colores de video estan producidos par combinaciones de cuatro ele- mentos (4 bits): tres componentes de color, (rojo, verde y azul) mas un com- ponente de intensidad. El resultado son 16 combinaciones de color. Los datos del buffer de video constan de valores de atributos de 4 bits. En el CGA, cada uno de estos valores se corresponde con uno de los 16 posibles colores. En el EGA, cad a valor de atributo design a un registro de los 16 posibles de la paleta, cad a uno de los cuales contiene un valor de color. Cada registro puede contener un color de 64 diferentes (se emplean 6 bits de color). En el MCGA, se utiliza un componente similar a la t>aleta del EGA, el DAC de video (convertidor digitallanal6gico de video), el cual contiene 256 registros de color. Cada registro es de 32 bits distribuidos de la forma siguiente: El byte mas significativo contiene ceros. Los siguientes, contienen el nivel de intensidad (0 a 63) de azul (A), verde (V) y rojo (R). Con un valor de atributo de 4 bits s610 se pueden utilizar 16 registros. Para hacer uso de los 256 registros se necesita utilizar un modo de video que utilice atri- butos de 8 bits. En el VGA, se utilizan 16 paletas, como la del EGA, y el DAC de video como en el MCGA. De este modo, un valor de atributo de 4 bits selecciona un registro de la paleta activa, cuyo valor selecciona a su vez uno de los 256 registros de color del DAC de video, cuyo contenido determina el color. Trabajar con un EGA, MCGA 0 VGA es, 16gicamente, mas complica- do que hacerlo con un CGA. Por ello, inicialmente el sistema carga los re- gistros de la paleta con los valores de color que coinciden con los colores disponibles en el CGA. De esta forma, utilizando modos de video compa- tibles con el CGA, veremos los mismos colores que obtendriamos con un CGA. Para cambiar la paleta y/o los colores utilizar las funciones rema- pallpalette( ) y remappalette( ).
  • 716. Las funciones graJicas necesitan informacion acerca de la posicion (coor- denadas x,y) donde se quiere dibujar. Estas coordenadas se pueden expre- sar de dos formas: El sistema de coordenadas fisicas es el establecido por omision. Tiene su origen en e1punta (0, 0). Los valores en este sistema son siempre positivos. El valor de x aumenta de izquierda a derecha y el valor de y de arriba a abajo. Los valores de x e y se expresan en puntos (pixels). Un sistema de coordenadas logicas es creado al mover el origen a una posi- cion determinada utilizando la funcion ~etvieworg( ). A partir de este instante nuestro origen (0, 0) estanl en la posicion indicada por esta fun- cion, por 10 que el resto de las funciones gnificas referinin los valores de coordenadas empleados, a este punto. Los valores de x e y, mantienen su orientacion. # include <stdio.h> # include <conio.h > # include <graph.h >
  • 717. main( ) ( / * Seleccionar la modalidad de video */ --.:setvideomode( --.MAXRESMODE); / * Determinar los parametros de la conjiguraci6n de video * seleccionada y almacenarlos en cv. */ ~etvideoconjig(&cv); xm cv.numxpixels/2-1; ym = cv.numypixels/2-1; / * centro eje x */ / * centro eje y */ / * Establecer un sistema de coordenadas 16gicas */ -----setvieworg(xm,ym); / * Pulsar una tecla para continuar */ getch( ); / * Restaurar la conjiguraci6n inicial */ -----setvideomode(---.DEFAULTMODE); } Este program a establece el origen de coordenadas (0, 0) en el centro de la pantalla y dibuja un cuadrilatero centrado en la misma. Para pasar de coordenadas fisicas a 16gicas, disponemos de la funci6n ~etviewcoord(x, y); y para pasar de coordenadas 16gicasa fisicas utilizar
  • 718. la funci6n ---I?etphyscoord(x, y). Los resultados son devueltos en la estruc- tura xycoord. Para utilizar las funciones graficas pensemos primero si hemos incluido la libreria gnlfica en el modelo de memoria que estemos utilizando. De no ser asi, realizar el enlace utilizando explicitamente esta libreria. Las decla- raciones para estas funciones estan en el fichero graph.h. Las podemos agru- par en funci6n de la tarea que desempefian, asi: _GCURSORON _GCURSOROFF cursor visible cursor no visible. Cuando se ejecuta un programa grafico, el cursor, por defecto, es visi- ble en modo texto y no visible en modo grafico.
  • 719. Selecciona la modalidad de video apropiada para la interface de video ins- talada en e1ordenador. El argumento modo es una constante de las especi- ficadas en la tabla expuesta en este mismo capitulo. Esta fund6n devuelve un valor distinto de 0 si la modalidad de video elegida es soportada, en caso contrario el valor devuelto es O. Esta funci6n devuelve el numero de filas actualmente puestas. Un va- lor 0 significa que el modo de video no es soportado. Puede suceder que e1valor Ii/as no sea soportado, 10 cual no significa un error. Almacena en una estructura de tipo videoconfig los parametros de la con- figuraci6n de video elegida.
  • 720. struct videoconfig ~ar * _far _getvideoconfig(struct videoconfig ~ar *cv); struct videoconjig cv; -ltetvideoconjig( &cv); Para configuraciones que soportan multiples pagmas de video ---.Setactivepage( ) espedfica el area de memoria donde son almacenados los resultados graficos procedentes de la ejecucion del programa. El argu- mento pag selecciona la pagina activa en un instante determinado. Por de- fecto es la pagina O. Esta fundon devuelve el numero de pagina anteriormente activa. Si ocurre un 'error la funcion devuelve un valor negativo. Con COA solamente se dispone de 16Kde RAM para soportar multi- ples paginas de video y solamente en modo texto. Los adaptadores EOA y VOA pueden soportar hasta 256K de RAM para multiples paginas de video en modo grafico. Para configuraciones que soportan multiples pagmas de video ---.Setvisualpage() selecdona la pagina de video a visualizar. Mientras tan- to el programa puede continuar e ir almacenando resultados graficos en otra pagina activa (ver fundon ---.Setactivepage(). El argumento pag es- pecifica la pagina a visualizar. Por defecto es la pagina O. Esta fundon devuelve el numero de pagina anteriormente visualiza- da. Si ocurre un error la funcion devuelve un valor negativo.
  • 721. # include <stdio.h> # include <graph.h > # include <conio.h > main( ) { int p = 0; while (!kbhit( )) / * repetir hasta pulsar una tecla */ ( / * alternar entre la pagina 0 y la 1 */ --setactivepage(p & 1); --setcolor(p % 16); -'ectangle(_GFILLINTERIOR, 90, 60, 230, 140); --setvi'sualpage(p + + & 1); 1 --setvideomode( --.DEFAULTMODE); } Este programa activa la pagina 0 6 1 y visualiza la 1 6 0 hasta pulsar una tecla. struct xycoord { short xcoord; coordenada x short ycoord; coordenada y } ~ar _setvieworg(short x, short y); nuevo origen
  • 722. Esta funcion devuelve en una estructura de tipo xycoord las coorde- nadas fisicas del origen logico anterior. struct xycoord ( short xcoord; short ycoord; ) -3ar _getviewcoord(short x, short y); coordenada x coordenada y coordenadas /fsicas Esta funcion devuelve en una estructura de tipo xycoord las coorde- nadas logicas resultantes. struct xycoord ( short xcoord; coordenada x short ycoord; coordenada y ) -3ar _getphyscoord(short x, short y); coordenadas /6gicas Esta funcion devuelve en una estructura de tipo xycoord las coorde- nadas fisicas resultantes.
  • 723. Limita el area de visualizaci6n de la pantalla al rectangulo definido por (xl, yl) y (x2, y2). El punta (xl, yl) corresponde ala esquina superior iz- quierda del rectangulo y el punta (x2, y2) corresponde a la esquina inferior derecha del rectangulo. ----.Setvideomode( ~RES4COLOR); ----.Setcliprgn(O, 0, 160, 100); / * area de visualizaci6n */ _ellipse(_GFILLINTERIOR, 110, 58, 210, 142); Este ejemplo limita el area de visualizaci6n al cuadrante superior iz- quierdo de la pantalla. La funci6n _ellipse( ) pinta un circulo centrado en la pantalla, del cual s610 podra verse el cuadrante superior izquierdo. Limita el area de visualizaci6n de la pantalla a un rectangulo definido por (xl, yl), esquina superior izquierda, y por (x2, y2), esquina inferior dere- cha y cambia al sistema de coordenadas 16gicas situando el origen en el punta fisico (xl, yl). ----.Setvideomode( ~RES4COLOR); ----.Setviewport(160, 100, 319, 199); Este ejemplo define como area de visualizaci6n el cuadrante inferior derecho de la pantalla y situa el origen 16gico en el punta fisico (160, 100).
  • 724. _remapallpalette(colores) y _remappalette(color, color_nuevo) Permiten cambiar los colores asignados alas paletas si el modo gnifico y el hardware 10 soportan. Los adaptadores EGA y VGA disponen de la ca- pacidad de asignar nuevos colores a una paleta. Mediante la funci6n ~emappalette( ) se puede cambiar un color y mediante la funci6n ~emapallpalette( ) se cambian todos los colores de una paleta. colores es un array con los valores de los colores. El primer valor en el array seria el nuevo color asociado con el color o. La funci6n _remapallpalette( ) devuelve un valor distinto de 0 si se ejecuta satisfactoriamente y un 0 en caso contrario. La funci6n _Jemappalette( ) devuelve el color anterior 0 un -1 si ocurre un error. Ejemplo: # include <stdio.h > # include <graph.h> long colores[16J=(JLACK, JLUE, _GREEN, -RED, -RED, J1AGENTA, JROWN, _WHITE, _GRAY, JIGHTBLUE, JIGHTGREEN, JIGHTRED, JIGHTRED, JIGHTMAGENTA, JIGHTYELLOW, JRIGHTWHITE J; main( ) I ----.Setvideomode(-MRES16COLOR); ~emapallpalette( colores); ----.Setcolor(3); ~ectangle(_GFILLINTERIOR, 110, 58, 210, 142); getch( ); ----.SetVideomode(---.DEFAULTMODE); J
  • 725. El array colores reasigna la paleta de colores por defecto de una EGA, de tal forma que los colores cyan y light cyan son visualizados como red y light red. Esta funcion trabaja solamente bajo las modalidades MRES4COLOR, MRESNOCOLOR y ORESCOLOR, permitiendo elegir una paleta de las definidas. El numero de paleta a elegir viene dado por el argumento num. La funcion -selectpalette() devuelve el numero de la pal eta anterior- mente elegida 0 un -1 si ocurre un error. -setvideomode( --.MRES4COLOR); ----.Selectpalette(2); ----.Setcolor(I); _..5etviewport(l60, 100, 319, 199); _ellipse(_GFILLINTERIOR, -50, -42, 50, 42); / * paleta elegida */ / * color 1 de la paleta 2 */ Este ejemplo visualiza el cuadrante inferior derecho de un circulo, en color verde claro, correspondiente a la paleta 2.
  • 726. En modo grcifico el color de fonda debe especificarse par media de la constante correspondiente. Devuelve como resultado el color de fonda actual. Par defecto este valor es O. Pone como color del primer plano el indicado par el argumento color. Par defecto este valor es el valor mas alto de la paleta can la que estemos tra- bajando. La funcion ---setcolor( ) devuelve como resultado el color previa a un -1 si ocurre un error. Devuelve como resultado el color actual del primer plano. Par defecto este valor es el valor mas alto de la paleta can la que estemos trabajando. Ejemplo: c = ---$etcolor( );
  • 727. Fija el tipo de linea a dibujar par otras funciones como _lineto( ) y -l'ectangle( ). El tipo de linea es fijado par una mascara de 16bits. Cada bit representa un punta en la linea. Si un bit es 1 se dibuja un punta y si es 0 no se dibuja. El argumento mascara es por defecto OxFFFF, 10 que da lugar a una linea continua. Este ejemplo fija la mascara 1100110011001100,10 que da lugar a li- neas discontinuas. Devuelve un numero correspondiente al valor actual de la mascara que se esta utilizando para trazar lineas por otras funciones. La mascara es un valor de 16bits donde un 1 significa dibujar un punta y un 0 no dibujarlo. En terminos mas tecnicos diriamos: si el bit es 1 el punta correspondiente se pone al color de la linea y si el bit es 0 el punta correspondiente se deja como esta, no cambia. Por defecto el valor de la mascara es OxFFFF. Este ejemplo almacena en la variable estilo el valor actual de la mas- cara utilizada para el trazado de lineas. Recubrir un area, de acuerdo con una mascara formada por un array de 8 por 8 bits, donde cada bit representa un punto. Si el bit es 1 el punta
  • 728. correspondiente se pone al color actual y si el bit es 0 el punta correspon- diente se deja como esta, no cambia. Par defecto el argumento mascara es NULL. unsignedchar *mascara= [ HxOOx3Fx30x30x3Cx30x30x30" J; ---setjillmask((char-far *)mascara); Devuelve el valor actual de la mascara formada por un array de 8 par 8 bits, utilizada para recubrir areas. Ver tambien la funci6n ---setjillmask( ). unsigned char *mascara= [ H xOO x3F x30 x30 x3C x30 x30 x30" J; char *masc = "12345678"; / * inicializar el array */ ~etjillmask(masc); ---setjillmask((char _far *)mascara); -----setcolor(2); --l"ectangle(_GFILLINTERIOR, 110, 57, 210, 142); -----setjillmask(masc); / * restaurar mascara */ / * salvar mascara actual */ / * mascara nueva */ getch( ); / * Pulsar una tec/a para continuar */ -----setvideomode(---.DEFAULTMODE); / * configuraci6n inicial */
  • 729. En la mascara, con el primer caracter se representa la primera linea, con el segundo caracter la segunda linea y as! sucesivamente. Para recubrir un area de acuerdo con una determinada mascara pri- mero se construye esta de la forma siguiente: Binario Hexadecimal Decimal Figura 00000000 00 0 00111111 3F 63 00110000 30 48 00110000 30 48 00111100 3C 60 00110000 30 48 00110000 30 48 00110000 30 48 10 cual se expresa de la forma: Recubre un area utilizando el color y la mascara actuales. Los argumentos x e y corresponden alas coordenadas de un punto. Si el punto esta dentro de la figura se recubre su interior y si esta fuera se recubre el exterior. El punto no debe estar sobre el borde de la figura. El argumento limite es una expresi6n numeric a que identifica el color utilizado para pintar el borde de la figura. El area a recubrir queda limitada por este borde.
  • 730. La fundon --floodjill( ) devuelve un valor distinto de 0 si se ejecuta satisfactoriamente 0 un 0 en caso contrario. unsigned char mascara[2][8] = {{0,66,36,24,24,36,66,0 }, [ 0,24,0,102,102,0,24,0}}; int i; char *masc = "12345678"; 1* Seleccionar la modalidad de video *1 ---.Setvideomode(-MAXRESMODE); 1* Dibujar un rectangulo y recubrirlo 2 veces *1 ~etjillmask(masc); 1* salvar mascara actual *1 ---.Setcolor(l); --.rectangle(_GBORDER, 109, 56, 211, 143); for (i = 0; i < 2; i+ +) [ ---.Setjillmask((char ~ar *)mascara[i]); I * mascara nueva *1 ---.Setcolor(i + 2); --floodjill(l60, 100, 1); 1* parar en el borde de color 1 *1 } ---.Setjillmask(masc); 1* restaurar mascara *1
  • 731. Mueve la posicion actual de salida gnifica al punta de coordenadas (x, y). No dibuja. struct xycoord [ short xcoord; short ycoord; J _far _moveto(short x, short y); coordenada x coordenada y nueva posicion La funcion _moveto( ) devuelve las coordenadas logicas de la posi- cion anterior en una estructura de tipo xycoord. Dibuja una linea desde la posicion actual hasta el punta (x, y). Cuando se utiliza la funcion -floodfil/( ) para recubrir una figura el borde debe ser una linea continua. La fundon _lineto( ) devuelve un valor distinto de 0 si la operacion se desarrolla satisfactoriamente 0 un 0 en caso contrario.
  • 732. Dibuja un rectangulo. Los puntos (xl, yl) y (x2, y2) corresponden alas esquinas superior izquierda e inferior derecha respectivamente. EI argumento c es una de las constantes siguientes: La funci6n _rectangle( ) devuelve un valor distinto de 0 si se ejecuta satisfactoriamente 0 un 0 en caso contrario. # include <stdio.h> # include <conio.h > # include <graph.h> main( ) { unsignedchar *mascara = {" xFO xFO xFO xFO xOF xOF xOF xOF"]; char *masc = "12345678"; / * inicializar el array */ static int c[ ] = { 10,20, 50,20, 55,25, 55,40, 50,45, 10,45, 50,45, 55,50, 55,65, 50,70, 10,70, 10,20 },o unsigned short estilo, n,o short color, jx, jy,o / * Seleccionar la modalidad de video */ ---.Setvideomode( -MAXRESMODE),o ---f5etvideoconjig(&cv),o / * almacenar conjiguraci6n */ / * Factores de escala en junci6n de la resoluci6n */
  • 733. fx = cv.numxpixels/320; fy = cv.numypixels/200; / '"Dibujar una diagonal */ _moveto(O,O); _lineto( cv.numxpixels-l, cv.numypixels-l); / * Dibujar una horizontal con formato */ estilo = ---I5etlinestyle( ); ---setlinestyle(OxFOF); _moveto(O, cv.numypixels/2); _lineto(cv.numxpixels-l, cv.numypixels/2); ---setlinestyle( estilo); / * Dibujar rectangulo coloreado */ ---I5etjillmask(masc); ---setjillmask((char far *)mascara); color = ---I5etcolor(); ---setcolor(2); -rectangle(_GFILLINTERIOR, 124x, 244y, 634x, 754y); ---setcolor(color); / * restaurar color */ ---setjillmask(masc); / * restaurar mascara */ / * salvar mascara actual */ / * mascara nueva */ / * Dibujar la letra B dentro del rectangulo */ _moveto(c[OJ4x, c[lJ4y); for (n = 0; n < 24; n + = 2) _lineto(c[nJ4x, c[n+l1*fy); ---settextposition(9, 94y); printj(HIENVENIDO A Microsoft e"); getch( ); / * Pulsar una tecla para continuar */ ---setvideomode(----.DEFAULTMODE); / * configuraci6n inicial */ J Dibuja una elipse. El centro de la elipse es el centro del rectangulo defini- do por los puntos (xl, yl) y (x2, y2). El argumento c es una de las constan- tes siguientes:
  • 734. La funci6n _ellipse( ) devuelve un valor distinto de 0 si se ejecuta sa- tisfactoriamente 0 un 0 en caso contrario. Dibuja un arco. El centro del arco es el centro del rectangulo definido por los puntos (xl, yl) y (x2, y2). El arco comienza en el punta de intersecci6n con el vector definido por (x3, y3) y finaliza en el punta de intersecci6n con el vector definido por (x4, y4). El arco es dibujado en sentido contra- rio alas agujas del reloj. short _far _arc(short xl, short yl, short x2, short y2, short x3, short y3, short x4, short y4); La funci6n _arc( ) devuelve un valor distinto de 0 si se ejecuta satis- factoriamente 0 un 0 en caso contrario. Dibuja un area limitada por un arco y dos radios. El centro del arco es el centro del rectangulo definido por los puntos (xl, yl) y (x2, y2) y los radios van desde el centro del arco a los puntos (x3, y3) y (x4, y4) respecti- vamente. El arco es dibujado en sentido contrario alas agujas del reloj. El argumento c es una de las constantes siguientes: short _far _pie(short c, short xl, short yl, short x2, short y2, short x3, short y3, short x4, short y4);
  • 735. La funci6n -pie( ) devue1ve un valor distinto de 0 si se ejecuta satis- factoriamente 0 un 0 en caso contrario. # include <stdio.h> # include <conio.h > # include <graph.h > main( ) ( short x, y, color; / * Seleccionar la modalidad de video */ --.Setvideomode( --.MAXRESMODE); ---l:etvideoconjig(&cv); / * almacenar conjiguracion */ / * Establecer coordenadas logicas */ x = cv.numxpixels/2 - 1; y = cv.numypixels/2 - 1; -setvieworg(x, y); -selectpalette(3 ); color = ---l:etcolor( ); / *paleta 3 */ / * color actual *! / * Dibujar rectdngulo */ -,"ectangle(_GBORDER, -80, -50, 80, 50); l* Dibujar sector */ -pie(_GBORDER, -60, -40, 60, 40, 0, -40, 70, 40); -setcolor(1); -floodjil/(-5, 0, color); / * color 1 */ / * colorear sector */ / * Colorear rectdngulo excepto sector */ -setcolor(2); -floodjil/(-55, -35, color);
  • 736. / * Pulsar una tecla para continuar */ getch( ); ~etvideomode( ---.DEFA ULTMODE); J Este ejemplo dibuja un rectangulo y en su interior un sector circular, coloreando ambas figuras. La funcion ~etpixel( ) devuelve las coordenadas del punta anterior- mente dibujado. Si la funcion falla devuelve el valor -1. La funcion ---I5etpixel() devuelve el color correspondiente al punta (x, y). Si la funcion falla devuelve el valor -1. Devuelve las caardenadas 16gicas de la posicion actual en una estructura de tipo xycoord. Esta funcion no es valida para texto (ver funciones para texto a continuacion). struct xycoord ( short xcoord; short ycoord; J _far _getcurrentposition( ); coordenada x coordenada y
  • 737. Fija el color para el texto. El argumento constante es un valor de 0 a 31. Los valores 0 a 15 corresponden a los colores normales y los valores 16 a 31 corresponden a los mismos colores, pero hacen que el texto parpadee. El color por defecto para el texto es el de valor mas alto. Da como resultado el color correspondiente a la posicion actual del cursor en el texto. Por defecto es el valor mas alto. Situa el cursor en la fila y columna indicada por los argumentos fila y col. Las salidas posteriores de texto producidas por la funcion _outtext( ) 0 por otras funciones seran colocadas a partir de ese punto. short row; short co); } ~ar --Settextposition(short fila, short co!); mlmero de fila mimero de columna La funcion ~ettextposition( ) devuelve la posicion anterior en una estructura de tipo rccoord definida en graph.h.
  • 738. Da como resultado la fila y columna de la posicion actual del cursor en el texto. EI resultado es devuelto en una estructura de tipo rccoord. struct rccoord { short row; short cot; } ~ar _gettextposition(void); mimero de fila mimero de columna Especifica la ventana donde va a ser visualizado todo el texto. Los argu- mentos (fl, c1) corresponden a la fila y columna de la esquina superior izquierda de la ventana y los argumentos (f2, c2) especifican la fila y co- lumna de la esquina inferior derecha de la ventana. EI texto es escrito a partir de la parte superior de la ventana. Cuando la ventana se llena se hace scroll automciticamente. Controla si el texto cubre una nueva linea 0 se trunca cuando se alcanza el borde de la ventana de texto definida. EI argumento opci6n puede ser una de las constantes siguientes: _GWRAPOFF _GWRAPON
  • 739. # include <stdio.h> # include <graph.h > struct videoconjig cv; char bujjer[1255}; main( ) [ struct rccoord pos_cursor, pos_inicial,· int color, c = 0; / * Se utiliza la modalidad por dejecto */ ---f5etvideoconjig(&cv); / * almacenar conjiguracion */ _cka~c~en(_GCLEARSCREENt ~ettextwindow(l, 15, 14, 50); / * ventana de texto */ _wrapon(_GWRAPOFF); / * texto no continua en una nueva linea */ color = ---f5ettextcolor( ); / * guardar el color original */ ~ettextcolor(color - 1); -.5ettextposition(l, 1); pos_cursor = ---f5ettextposition( ); pos_inicial = pos_cursor; while (pos_cursor.row < 20) [ / * inicializar variable */ / * salvar posicion inicial */ c + = sprintj(bujjer + c, "Fila = 0/02d,Col = %d n': pos_cursor.row+ +, pos_cursor.col); } ~ettextposition(pos_inicial.row, pos_inicial.col); _outtext(bujjer ); ~ettextcolor(color); / * restaurar color original */ _outtext("Penultima lfnea. La siguiente linea no se trunca"); / * Una especijicacion juera de los limites de la ventana, situa
  • 740. * el cursor al principio de la ultima llnea de la misma */ ----.Settextposition(21,51); _wrapon(_GWRAPON); / * texto continua en una nueva llnea */ _outtext(C< nUltima llnea. Esta Unea es demasiado larga. n "); J Este programa crea una ventana para texto y escribe sobre ella. Una vez definida la ventana, las referencias hechas a fila y columna para situar el cursor se miden con respecto a los bordes de la ventana. Tambh~nutiliza la funcion _wrapon( ) con el fin de ver el efecto que produce en sus dos modalidades. Observar que no se ha definido una modalidad de graficos, por no ser necesario cuando se trabaja solamente con texto. Almacena en el area de memoria apuntada por imagen, la figura de la pan- talla encerrada en un rectangulo definido por los puntos (xl, yl) y (x2, y2). El area de memoria debe ser 10 suficientemente grande como para conte- ner la figura. El tamafio puede ser determinado por la funcion _imagesize( ). void _far _getimage(short xl, short yl, short x2, short y2, char _far *imagen); Da como resultado el numero de bytes necesarios para almacenar la figura definida dentro del rectangulo especificado por las coordenadas (xl, yl) y (x2, y2). Este tamafio es determinado por la siguiente formula: x = abs(xl - x2) + 1; y = abs(yl - y2) + 1; t = 4 + ((long)((x * bits~or~ixel + 7) /8) * (long)y);
  • 741. EI valor de bits-por -pixel es devuelto por la funci6n --f5etvideoconfig( ) en el campo bitsperpixel. La fund6n _imagesize( ) devue1veel numero de bytes necesarios para almacenar la figura. buffer = (char *)malloc((unsigned int) _imagesize(O, 0, 80, 50)); if (buffer = = (char *)NULL) exit(-l); Transfiere a la pantalla la figura almacenada en la zona de memoria apun- tad a por imagen, colocando la esquina superior izquierda del rectangulo que contiene dicha figura en el punta (x, y). EI argumento accion, es un parametro utilizado para superponer 0 transformar imagenes, con otras imagenes ya en pantalla. su fund6n es opuesta a --f5etimage( ). Da lugar a una copia exacta de la imagen almacenada. es la misma que _GPSET, excepto que produce una imagen negativa. ejecuta la operad6n AND entre la imagen almacenada y la de la pantalla. Se utiliza para transferir una ima- gen encima de una ya existente sobre la pantalla.
  • 742. ejecuta la operacion OR entre la imagen almacenada y la de la pantalla. Se usa para superponer la imagen sobre otra ya existente. ejecuta la operacion XOR entre la imagen almacenada y la de la pantalla. Es un modo especial utilizado a me- nudo para animaci6n. La animacion de un objeto se realiza de acuerdo con la siguiente secuencia de pasos: 3. Borrar la imagen de la pantalla (-putimage( )) y dibujarla en la nueva posicion. Una imagen se borra ejecutando -putimage( ) con XOR por segunda vez en la misma posicion. La animacion tambien puede ejecutarse utilizando la opcion PSET, teniendo la precaucion de que una nueva imagen borre la anterior. En este ultimo caso, el rectangulo debe ser suficiente, para que ademas de recoger la imagen, recoja tambien el desplazamiento de la misma. Los siguientes ejemplos, muestran con claridad 10 anteriormente ex- puesto. El siguiente ejemplo visualiza el resultado que se obtiene al desplazar un bola a 10 ancho de la pantalla, utilizando los cinco modos de accion
  • 743. (PSET, PRESET, XOR, OR y AND). Observar que la animaci6n real se produce cuando se ejecuta la sentencia: buffer almacena la matriz de pixels correspondientes a la imagen y al desplazamiento de la misma. / * Funciones para animaci6n de figuras: * _imagesize --Itetimage -putimage */ # include <conio.h > # include <stddefh> # include <stdlib.h> # include <malloc.h > # include <graph.h> short accion[5J = { _GPSET, _GPRESET, _GXOR, _GOR, _GAND }; char ~escrip[5J = { "PSET': "PRESET: "XOR ': "OR ': "AND " }; main( ) ( char far *buffer; size_t t_imagen; short i, x, y = 0; / * Seleccionar la modalidad de video */ -----setvideomode(--.MAXRESMODE); / * Almacenar configuraci6n */ --Itetvideoco~ig(&cv); / * Animaci6n de figuras */ -----setcolor(3); for (i = 0; i < 5; i+ +) { x = 50; y + = 35;
  • 744. ----settextposition(1, 1); _outtext( descrip[i]); / * Dibujar y desplazar una elipse */ _ellipse(_GFILLINTERIOR, x-15, y-15, x+15, y+15); t_imagen = (size_tJ_imagesize(x-16, y-16, x+ 16, y+ 16); buffer = (char far *)-fmalloc(t_imagen); if (buffer = = (char far *)NULL) exit(!----setvideomode( ---.DEFAULTMODE)); / * Almacenar la elipse en el buffer */ ----f5etimage(x-16.y-16, x+16, y+16, buffer); / * Mover la elipse con una determinada acci6n */ while (x < cv.numxpixels-60) [ x += 1; -putimage(x-16, y-16, buffer, accion[i]); } -ffree(buffer ); getch( ); } exit(!----setvideomode( ---.DEFAULTMODE)); } / * Liberar memoria */ / * pulsar una tecla para continuar */ El siguiente ejemplo simula una pelota rodando. En este caso, se utiliza la funci6n -putimage( ) con la opci6n XOR. Observar que el rectangulo para leer la figura es ahora mas pequefio, esto es, los lados son tangentes al circulo que forma la pelota. 3. Borrar la imagen: -putimage() con la opci6n _GXOR, en la mis- ma 10calizaci6n del punta 1.
  • 745. 4. Volver al punto 1, para dibujar la imagen en la nueva localizaci6n ca1culada. # include <stdio.h> # include <conio.h > # include <graph.h > # include <malloc.h > struct videoconfig cv; char *buffer; / * utilizado con ~etimage y con -putimage */ main( ) ( size_t t_imagen; int x=O, i=O; / * Seleccionar la modalidad de video */ --.Setvideomode( --.MAXRESMODE); ~etvideoconfig(&cv); /* almacenar configuraci6n ;,/ _ellipse(_GFILLINTERIOR, 0, 96, 8, 104); / * dibujar pelota */ t_imagen = (size_t}_imagesize(O, 96, 8, 104); buffer = (char *)malloc(t_imagen); if (buffer = = (char *)NULL) exit(!--.Setvideomode( ----.DEFAULTMODE)); 1* Almacenar la imagen en el buffer */ ~etimage(O, 96, 8, 104, buffer); / * Desplazar la pelota a 10 ancho de la pantalla */ do ( -putimage(x, 96, buffer, _GXOR); -putimage(x+ =2, 96, buffer, _GXOR); for (i = 1; i < 3000; i+ +); } while (x < cv.numxpixels-10); / * borrar pelota */ / * dibujar pelota */ / * retardo */
  • 746. getch( ); free(buffer ); / * Pulsar una tecla para continuar */ / * Liberar memoria */ ---.Setvideomode(-,,-DEFAULTMODE); } EI siguiente ejemplo presenta una pe10ta que rebota al chocar contra una barrera. Observar la utilizaci6n de la funci6n, ---!Jetpixel( ). # include <stdio.h > # include <conio.h > # include <graph.h> # include <malloc.k> struct videoconfig cv; char *buffer; / * utilizado con ---!Jetimage y con -putimage */ main( ) [ size_t t_imagen; / * tamano de la imagen */ iot x, i, posicion, altura, direcci6n; iot x1=1, y1=96, x2=9, y2=104, d=x2-x1; / * Seleccionar la modalidad de video */ ---.Setvideomode(-MAXRESMODE); ---!Jetvideoconfig(&cv); / * almacenar configuraci6n */ / *Posici6n y altura de la barrera */ printf(HPosici6n de la barrera de 10 a %d= > ':cv.numxpixels-20); scanj(H%d': &posici6n); printf(HAltura de la barrera de 1a %d = > ':cv.numypixels-1); scanj(H%d': &altura); _clearscreen( _GCLEARSCREEN); ---.Setcolor(2); ---fectangle( _GFILLINTERIOR, posicion, 0, posicion +20, altura);
  • 747. -----setcolor(l); _ellipse(_GFILLINTERIOR, xl, y1, x2, y2); / * dibujar pelota */ t_imagen = (size_t)_imagesize(x1-1, y1-1, x2 +1, y2 +1); buffer = (char *)malloc( t_imagen ); if ( buffer = = (char *)NULL) exit( !-----setvideomode(---.DEFAULTMODE ) ); / * A lmacenar la imagen en el buffer */ ---f5etimage(x1-1,y1-1, x2+ 1, y2+ 1, buffer); / * Mover la pelota por la pantalla. Si choca contra la * barrera rebota */ direcci6n = 1; x = d; do { / * Si no hay contacto con la barrera, getpixel devuelve * el color de fondo. En caso contrario devuelve el color * de la barrera. */ if (---f5etpixel(x+3, y1-1) != 0) direcci6n = -1; x + = direcci6n; -putimage( x-d, y1-1, buffer, _GPSET); for (i = 1; i < 2000; i+ +); J while (x < cv.numxpixels-d && (x > d II direcci6n = = 1)); / * 1 = derecha, -1 = izquierda */ / * coordenada x de la pelota */ /* pelota */ / * velocidad */ getch( ); freer buffer ); / * Pulsar una tecla para continuar */ / * Liberar memoria */ -----setvideomode(---.DEFAULTMODE); J Realizar un programa que simule los movimientos de una bola rodan- do sobre una mesa de billar.
  • 748. Amilisis: dibujar la bola utilizar ---$etimage( ) para almacenar la bola hacer PosicionActual = PosicionAnterior = PuntoDeComienzo DO Borrar (-putimage( ) con XOR) la figura de la posicion anterior PosicionActual = PosicionActual + Incremento Visualizar (-putimage( ) la figura en la posicion actual Esperar un tiempo pequeno hacer PosicionAnterior = PosicionActual WHILE no se pulse una tecla fin del programa # include <stdio.h > # include <conio.h > # include <graph.h > # include <ma/loc.h > # include <stdlib.h > struct videoconjig cv; char ~ar *bola; main( ) [ size_t t_imagen; / * tamafio de la imagen */ short retar, Max----.X, Max_Y, Min----.X, Min_Y,· short RadioEola, Inicio----.X, Inicio_Y,· short PosicionActual----.X, PosicionActual_Y,· short PosicionAnterior ----.X,PosicionAnterior -Y,. short Incremento----.X, Incremento_Y, Direccion----.X,Direccion_Y,· / * Seleccionar la modalidad de video */ ---.Setvideomode(--.MAXRESMODE); ---$etvideoconjig(&cv); / * almacenar conjiguracion */ ---.Setbkcolor(_GREEN); / * Valor minimo y maximo de las coordenadas de panta/la */ Max----.X = cv.numxpixels-l; Min----.X = 0; Max_Y = cv.numypixels-l; Min_Y = 0;
  • 749. / * Dibujar l[mites de la pantalla */ ---fectangle(_GBORDER, Min---.X, Min_Y, Max---.X, Max_Y); / * Fijar el radio de la bola */ RadioBola = 12; / * Fijar la posicion inicial de la bola */ Inicio----.X = RadioBola + 1; Inicio_Y = RadioBola + 1; / * Dibujar la bola sobre la pantalla */ _ellipse( _GFILLINTERIOR, Inicio----.X - RadioBola, Inicio_Y - RadioBola, Inicio----.X + RadioBola, Inicio_Y + RadioBola); / * Almacenar la figura en el array bola */ t_imagen = (size_tJ_imagesize( Inicio----.X - RadioBola, Inicio_Y - RadioBola, Inicio----.X + RadioBola, Inicio_Y + RadioBola); bola = (char *)malloc(t_imagen); if (bola = = (char *)NULL) exit(L..setvideomode( --.DEFAULTMODE)); / * A lmacenar la imagen en bola */ ~etimage(Inicio----.X - RadioBola, Inicio_Y - RadioBola, Inicio----.X + RadioBola, Inicio_Y + RadioBola, bola); / * Inicializacion */ PosicionActual----.X = Inicio---.X,· PosicionActual_Y = Inicio_Y; PosicionAnterior ----.X = Inicio----.X- RadioBola; PosicionAnterior_Y = Inicio_Y - RadioBola; Direccion----.X = 1; Direccion_Y = 1; / * 1 = derecha, -1 = izquierda */ / * 1 = hacia abajo, -1 = hacia arriba */ do [ / * Borrar la bola anterior */ -putimage(PosicionAnterior ---.X, PosicionAnterior_ Y,bola,_GXOR);
  • 750. / * Calcular la nueva posicion de X * si borde derecho poner direccion hacia borde izquierdo .* si borde izquierdo poner direccion hacia borde derecho * si la bola choca con un borde, realizar un pitido */ Incremento----..X = rand( ) % RadioBola; if (PosicionActual----..X+ Incremento----..X+ 2 * RadioBola > Max----..X) ( Direccion----..X= -1; putchart x07'); J if (PosicionActual----..X- Incremento----..X < Min_YJ ( Direccion----..X= 1; putchart x07'); J PosicionActual~ =PosicionActual~ + (Incremento~ *Direccion~); / * Calcular la nueva posicion de Y * si borde inferior poner direccion hacia borde superior * si borde superior poner direccion hacia borde inferior * si la bola choca con un borde, realizar un pitido */ Incremento_Y = rand( ) % RadioBola; if (PosicionActual_Y + Incremento_Y + 2 * RadioBola > Max_Y) ( Direccion_Y = -1; putchart x07'); J if (PosicionActual_Y - Incremento_Y < Min_Y) ( Direccion_Y = 1; putchart x07'); J PosicionActual_ Y=PosicionActual_ Y+(Incremento_ Y*Direccion_ Y); * Visualizar la bola en la nueva posicion */ -putimage(PosicionActual-.-X, PosicionActual_Y, bola, _GXOR); for (retar = 1; retar < 4000; retar+ +); / * retardo */ / * La posicion actual pasa a ser posicion anterior */ PosicionAnterior ----..X= PosicionActual----..X; PosicionAnterior _Y = PosicionActual_Y; J while (!kbhit( )); / * repetir hasta pulsar una tecla */
  • 751. ---setvideomode( ----DEFAULTMODE); } Las funciones que refieren sus coordenadas a un sistema de coordenadas fisico 0 16gico, requieren valores enteros. En ocasiones necesitaremos re- presentar valores reales y dentro de unos limites. Estos valores, al represen- tarlos utilizando toda la pantalla 0 una ventana, necesitanin en la mayoria de los casos de la aplicaci6n de un factor de escala. La funci6n ---setwindow( ) permite de una forma sencilla realizar estas operaciones. Define un sistema de coordenadas gnificas reales (coordenadas cartesia- nas) sobre una ventana 0 en su defecto sobre toda la pantalla. Los argu- mentos (wxl, wyl), especifican la esquina superior izquierda de la ventana sobre la que se encuadra el sistema de coordenadas y los argumentos (wx2, wy2), especifican la esquina inferior derecha de esta ventana. El origen de coordenadas es el (0, 0). El argumento inver puede tomar como valores: establece el sistema de coordenadas cartesianas haciendo que "y" aumente de abajo a arriba de la pantalla. establece el sistema de coordenadas cartesianas haciendo que "y" aumente de arriba a abajo de la pantalla. short ~ar ----setwindow(shortinver, double wxl, double wyl, double wx2, double wy2); Esta funci6n devuelve un valor distinto de 0 si se ejecuta satisfactoria- mente 0 un 0 en caso contrario.
  • 752. # include <stdio.h > # include <conio.h > # include <graph.h > # include <math.h > # define TR UE 1 main( ) ( double pi, alja, valor1, valor2, paso, radio, X Y,. / * Seleccionar la modalidad de video */ ---.Setvideomode(--.MAXRESMODE); pi = atan(J.O) * 4; valor1 = 5.0; valor2 / * definir el valor del mimero pi */ 6.0; paso = 1000.0; / * Sistema de coordenadas reales */ ---.Setwindow(TRUE, -1, -1, 1, 1); / * Representaci6n grdfica */ for (alja = 0; alja < = 2 * pi; alja + = 2 * pi / paso) ( radio = cos(2 * alja); x = radio * cos(valor1 * alja); Y = radio * sin(valor2 * alja); ---.Setpixel_w(X Y); } getch( ); / * Pulsar una tecla para continuar */ ---.Setvideomode(---.DEFAULTMODE); /* configuraci6n inicial */ }
  • 753. # include <stdio.h> # include <conio.h > # include <graph.h > # include <math.h > # define TR UE 1 main( ) { double X-ftlin, X-ftlax, Y-ftlin, Y-ftlax, x: Y, incremento; printjttEntre que valores de X esta comprendida la funci6n n"); printj("X mfnima X maxima: "); scanf("%lj %lj': &X-ftlin, &X-ftlax); printj("Entre que valores de Y esta comprendida la funci6n n "); printj("Y mfnima Y maxima : "); scanf("%lj %lj': &Y-ftlin, &Y-ftlax); / * Seleccionar la modalidad de vfdeo */ ---setvideomode( --.MAXRESMODE); ~etvideoconfig(&cv); / * almacenar configuraci6n */ / * Establecer el sistema de coordenadas cartesianas */ ---setwindow( TRUE, X-ftlin, Y-ftlin, X-ftlax, Y_max ); / * Dibujar ejes */ --"loveto_w(X--"lin, 0);~ineto_w(X--"lax, 0); --"loveto_w(O, Y--"lin); ~ineto_w(O, Y--"lax); / *Dibujar eje X */ / * Dibujar eje Y */ / * Representaci6n grafica */ incremento = (X_max - X-ftlin) / cv.numxpixels;
  • 754. for ( X = X_min; X < = X-ftlax; X + = incremento) ( / * Funci6n a representar */ Y = 2 * pow(cos(X), 2) - sin(5 + X); ---setpixel_w(X, Y); /* dibujar el punto (x, y) */ } getch( ); / * Pulsar una tecla para continuar */ ---setvideomode(-DEFAULTMODE); /* conjiguraci6n inicial */ } / * La funci6n matherr es automaticamente llamada si ocurre * un error en una funci6n matematica. */ int matherr(struct exception ~rr) ( printf(HError en funci6n: %s(%g) n': err->name, err->argJ); printf(HPulse una tecla para continuar"); getch( ); exit( ---setvideomode(-DEFAULTMODE) ); } En estos ejemplos, los valores dentro de los limites establecidos, se re- presentan a escala sobre toda la pantalla. Si quisieramos que la representa- cion ocurriera sobre una ventana determinada, habria que definir previa- mente esta por medio de la funcion ---setviewport( ). struct _wxycoord { double wx; double wy; } _far _getwindowcoord(short x, short y); coordenada cartesiana x coordenada cartesiana y coordenadas j{sicas
  • 755. FUNCIONES PARA UN SISTEMA DE COORDENADAS CARTESIANAS (WINDOW) Cuando se define un sistema de coordenadas cartesianas para trabajar con valores reales, tenemos que utilizar funciones cuyos panimetros sean rea- les. Todas estas funciones finalizan con _w 0 con _wxy. Estas funciones ya han sido comentadas anteriormente, pero pensan- do en un sistema de coordenadas ffsico (coordenadas de pantalla) 0 16gico (el origen 10situamos sobre un punta cualquiera de la pantalla). A conti- nuaci6n las exponemos, pensando ahara en un sistema de coardenadas car- tesiano (---setwindow( ). Las declaraciones para todas estas funciones es- tan en el fiehero graph.h. Muchas de estas funciones utilizan 0 devuelven valores definidos en una estructura de tipo: struct _wxycoord [ double wx; double wy; }; coordenada cartesiana x coordenada cartesiana y struct xycoord ~ar _getviewcoord_wxy(struct _wxycoord ~ar pwxy); short _far -fectangle_w(short c, double wxl, double wyl, double wx2, double wy2); short _far -fectangle_wxy(short c, struct _wxycoord pwxyl, struct _wxycoord pwxy2); short ~ar _ellipse_w(short c, double wxl, double wyl, double wx2, double wy2);
  • 756. short _far _ellipse_wxy(short c, struct _wxycoord pwxyl, struct _wxycoord pwxy2); short _far _arc_wxy(struct _wxycoord pwxyl, struct _wxycoord pwxy2, struct _wxycoord pwxy3, struct _wxycoord pwxy4); short _far _pie_wxy(short c, struct _wxycoord pwxyl, struct _wxycoord pwxy2, struct _wxycoord pwxy3, struct _wxycoord pwxy4); void _far _getimage_w(double wxl, double wyl, double wx2, double wy2, char _far *imagen); void _far _getimage_wxy(struct _wxycoord pwxyl, struct _wxycoord pwxy2, char _far *imagen); long _far ~magesize_w(double wxl, double wyl, double wx2, double wy2); long _far ~magesize_wxy(struct _wxycoord pwxyl, struct _wxycoord pwxy2); void ~far _putimage_w(double wxl, double wyl, double wx2, double wy2, char _far *imagen, short accion); El siguiente ejemplo representa un conjunto de valores sobre un siste- ma de coordenadas cartesianas. La representaci6n se hace sobre tres venta- nas cuadriculadas de diferentes tamafios (efecto ampliar/reducir), incluyendo texto. # include <stdio.h > # include <conio.h >
  • 757. # define TR VE 1 #define FALSE 0 struct videoconfig cv; void cuadricular_dibujar(void); double val! J = ( -0.3, -0.2, -0.224, -0.1, -0.5, 0.21, 2.9, 0.3, 0.2, 0.0, -0.885, -J.1,-0.3, -0.2, 0.001, 0.005, 0.14, 0.0, -0.9, -0.13, 0.3 }; main( ) ( void cuadricular_dibujar(void); int xmedia, ymedia; int pejex, pejey, cols, fi/as; struct _wxycoord esizda, eidcha; ---setvideomode( --.MAXRESMODE); ---1Jetvideoconfig(&cv); / * almacenar configuraci6n */ _clearscreen( _GCLEARSCREEN); pejex = cv.numxpixels; pejey = cv.numypixels; xmedia = pejex / 2; ymedia = pejey / 2; cols = cv.numtextcols; fi/as = cv.numtextrows; ---setviewport(O, 0, xmedia-l, ymedia-1); ---settextwindow(l, 1, filas/2, cols/2); ---setwindow(FALSE, -2.0, -2.0, 2.0, 2.0); cuadricular _dibujar( );
  • 758. ~etviewport(xmedia, 0, pejex-I, ymedia-I); ~ettextwindow(I, eols/2 +1, ji/as/2, cols); ~etwindow(FALSE, -3.0, -3.0, 3.0, 3.0); euadrieular_dibujar( ); -Teetangle_w(_GBORDER, -3.0, -3.0, 3.0, 3.0); ~etviewport(O, ymedia, pejex-I, pejey-I); ~ettextwindow(fi/as/2 +2, 1, ji/as, cols); ~etwindow(TRUE, -3.0, -1.5, 1.5, 1.5); euadrieular_dibujar( ); esizda.wx = -3.0; esizda.wy = -1.5; eideha.wx = 1.5; eideha.wy = 1.5; _reetangle_wxy(_GBORDER, &esizda, &eideha); geteh( ); / * Pulsar una tecla para eontinuar */ ~etvideomode(----.DEFAULTMODE); / * eonjiguraci6n inicial */ J void euadrieular_dibujar(void) { int i, neolores, xl, yI, x2, y2; double x, y; char texto[80}; for (i = 1; i < neolores; i+ +) { ~ettextposition(i, 2); ~ettexteolor(i); sprintj(texto, HColor O/Od':i); _outtext(texto ); J
  • 759. ---setcolor(l); -.Jectangle_w(_GBORDER, -1.0, -1.0, 1.0, 1.0); -.Jectangle_w(_GBORDER, -1.02,-1.02,1.02, 1.02); for (x = -0.9, i = 0; x < 0.9; x + = 0.1) ( ---setcolor(2); ---fl1.oveto_w(x, -1.0); _lineto_w(x, 1.0); ---fl1.oveto_w(-1.0, x); _lineto_w( 1.0,x); ---setcolor(3); ---fl1.oveto_w(x - 0.1, valli+ +J); _lineto_w(x, valli]); } ---fl1.oveto_w(0.9, valli + +J); _lineto_w(1.0, valli]); } El resultado que se obtiene al ejecutar este programa se muestra en la figura siguiente:
  • 760. I .,~.~n 1~l)lor ;: Color J Color" : Coler 5 I' Coler G !L r.nl.l' 7 i,- CIJlul' 8 l~ill.Rr ~ Color 10 i Coler 11 Coler lZ 'L. - Coler 13 r.nl.l' 14 Cul.r 15 I~olo! ;: Color J Color" r.nloT·!i Cul.r , r.nl.r 7 CIJlul' 8 Color !I Color 10 Color 11 r.nl.r 12 Cul.r 13 r.nl.r 15 J .•• ll ,ur L:Dlor ~ Color 3 [;0111I' !I Go I.. 1i Col.' Ii r.nl. i Culur B r.nlnr ~ Color 10 [;01.' 11 Gol.. lZ CDI•• 13 r.nl.14 Cui •• I!I .I -= __ 1.... ,"","'.' ~ __ '~,L .. . IT"" ~-···r··--I I ( I I l II ,I , II
  • 761. Microsoft C dispone de unas pocas funciones que permiten presentar gni- ficamente un conjunto de datos. Una presentaci6n gnifica se puede hacer utilizando 10ssiguientes tipos de diagramas: diagrama de barras horizon- tales, diagrama de barras verticales, diagrama de sectores, diagrama de li- neas y diagram a de puntos. ESTRUCTURA DE UN PROGRAMA PARA PRESENTACIONES GRAFICAS Para escribir un programa C que utilice funciones para presentaciones gra- ficas, seguir 10s siguientes pasos: 1. Incluir 10s ficheros GRAPH.H y PGCHART.H, asi como cual- quier otro fichero .h que necesite el programa. 2. Activar la modalidad de video para grcificos (----.Setvideomode()) e inicializar el sistema de presentaciones grcificas, -pg_initchart( ).
  • 762. 3. Almacenar en una estructura de tipo chartenv,los parametros que definen la presentacion grafica sobre la pantalla. La funcion -pg_dejaultchart( ) los asigna por defecto. La definicion de esta estructura, as! como de las estructuras que la componen, se encuentran declaradas en el fichero pgchart.h. Todas estas estructuras pueden visualizarse facilmente a traves del menu de ayuda del PWB de Microsoft C; sera necesario acceder a sus miembros cuando deseemos modificar los valores asignados por defecto. typedef struct [ short charttype; / * JGJAR, JG_COLUMN, JGJINE, JG-.SCATTER, JGJIE "*/ short chartstyle; / * Estilo para el tipo de grdfico seleccionado */ windowtype chartwindow; / * Definicion de la ventana para el grdfico completo */ windowtype datawindow; / * Definicion de la ventana para la parte de datos del grdfico */ titletype main title; / * TItulo principal del grdfico */ titletype subtitle; / * Subtftulo del grdfico */ axistype xaxis; / * Definicion para el eje X */ axistype yaxis; / * Definicion para el eje Y */ legendtype legend; / * Definicion para la leyenda */ J chartenv; 4. Almacenar los datos a representar en arrays, ya que las funciones para presentaciones graficas los referencian mediante punteros. Los datos pueden provenir de diferentes medios: de ficheros, de cal- culos 0 directamente del teclado.
  • 763. Estas funciones devuelven un 0 si se ejecutan satisfactoriamente y un valor distinto de 0, en caso contrario. Inicializa el color y estilos de linea, paletas, modos de pantalla y tipos de caracteres. Esta funcion debe ser la primera en llamarse. Inicializa por defecto todas las variables contenidas en una estructura de tipo chartenv, necesarias para el gnifico a realizar. Constante predefinida _PG_BARCHART 1 _PG_COLUMNCHART 2 _PG_LINECHART 3 _PG_SCATTERCHART 4 .-PG_PIECHART 5 Tipo barras horizontales Tipo barras verticales Tipo lineas Tipo puntos Tipo sectores estilo es un valor 1 0 2. Cada uno de los cinco tipos de gnificos, puede aparecer en dos estilos diferentes:
  • 764. Barras H. Barras V. Lineas Puntos Sectores Lado a lado Lado a lado Puntos con lineas Puntos con lineas Con porcentajes Apiladas Apiladas Puntos solamente Puntos solamente Sin porcentajes Las constantes asociadas a los valores 1 y 2 para cada esti- 10, son las siguientes: Constante predefinida Valor Significado PG_PLAINBARS 1 Estilo Barras lado a lado _PG_STACKEDBARS 2 Estilo Barras apiladas _PG_POINTANDLINE 1 Estilo Puntos y Lineas _PG_POINTONLY 2 Estilo Puntos solamente _PG_PERCENT 1 Estilo Sectores con 070 _PG_NOPERCENT 2 Estilo Sectores sin % Presenta un diagrama para una unica serie de datos. El diagrama puede ser de barras 0 de lineas, dependiendo esto del tipo especificado en la es- tructura ent. short _far _pg_chart(chartenv _far * ent, char * _far * elementos, float _far * valores, short n); elementos array que contiene los elementos para los cuales se quieren re- presentar los valores. Por ejemplo, paises, empresas, meses.
  • 765. array que contiene los datos que queremos representar gnifi- camente y que se corresponden con los elementos anteriores. short _far _p~chartpie(chartenv _far * ent, char * _far * elementos, float _far * valores, short _far *explotar, short n); elementos array que contiene los elementos para los cuales se represen- tan los valores. explotar array de n valores 0 6 1.Un 1indica separar (explotar) ese sec- tor de los otros. Un 0 indica no separarlo. # include <conio.h > # include <stdlib.h > # include < graph.h > # include <string.h> # include <pgchart.h> # define PAISES 6 float ~ar valor[PAISESj = {53.1, 41.8, 19.5, 13.7, 10.8, 20.(jJ; char ~ar *elementos[PAISESj = {"Italia': "Espana': "Grecia': "Tunicia': "Turqu{a': "Otras"}; short ~ar explotar[PAISESj = { 0, 1, 0, 0, 0, °};
  • 766. main( ) { / * Modo grajico de mas alta resoluci6n */ if (!---setvideomode(-MAXRESMODE)) exit( 1 ); / * Grajicos no disponibles */ -pg_dejaultchart(&ent, ~G~ARCHART, ~G~LAINBARS); strcpy(ent.maintitle.title, "Producci6n de aceite de oliva"); -pg_chart(&ent, elementos, valor, PAISES); getch( ); _clearscreen( _GCLEARSCREEN); -pg_dejaultchart(&ent, ---.PG_COWMNCHART, ---.PG~LAINBARS); strcpy(ent.maintitle.title, "Producci6n de aceite de oliva"); -pg_chart(&ent, elementos, valor, PAISES); getch( ); _clearscreen( _GCLEARSCREEN); -pg_dejaultchart(&ent, ~G~IECHART, ~G~ERCENT); strcpy(ent.maintitle.title, "Producci6n de aceite de oliva"); -pg_chartpie(&ent, elementos, valor, explotar, PAISES); getch( ); exit(!---setvideomode(---.DEFAULTMODE)); }
  • 767. _P9_chartms (ent,e Iementos,va I0res, nse rieS,n ,co Iumnas, eti_series) Genera un gnifico para multiples series de datos. El diagrama puede ser de barras, de lineas, 0 de puntos, dependiendo esto del tipo especificado en la estructura ent. short ~ar _p~chartms(chartenv _far * ent, char * _far * elementos, float ~ar * valores, short nseries, short n, short columnas, char * _far * etL...series); elementos array que contiene los elementos para los cuales se represen- tan los valores. eti_series array de etiquetas correspondientes a los valores que se indi- can en cada serie. # include <conio.h > # 'include <graph.h > # include <string.h > # include <pgchart.h> # include <stdlib.h > / * Observar que los datos son declarados en un array * multidimensional. Como las funciones para representaciones * graficas multiples esperan arrays simples, habra que
  • 768. * emplear un tipo cast en la llamada a la funcion. */ #define EQUIPOS 4 # define MESES 3 float _far valores[EQUIPOSj[MESESj = {[ 453, 522, 617 ], { 503, 440, 585 ], { 713, 642, 477 ], { 392, 464, 411 )1. char _far *meses[MESESj = { "Mayo':"]unio':"]ulio" ]; char _far ~quipos[EQUIPOSj = {t~lfa':"Verdes':"]avis':t~tlas"]; main( ) { chartenv ent; / * Modo grafico de mas alta resolucion */ if (L...setvideomode(--.MAXRESMODE)) exit(l); / * Graficos no disponibles */ -pg_defaultchart(&ent, ---.PG----.BARCHART, ---.PG---.PLAINBARS); strcpy(ent.maintitle.title, "Registros liga de Golf"); -pg_chartms(&ent, meses, (float _far *)valores, EQUIPOS, MESES, MESES, equipos); getch( ); _clearscreen( _GCLEARSCREEN); -pg_defaultchart(&ent, -PG_COWMNCHAKI; -PG---.PLAINBARS); strcpy(en t.main title.title, "Registros liga de Golf"); -pg_chartms(&ent, meses, (float _far *)valores, EQUIPOS, MESES, MESES, equipos); getch( ); _clearscreen( _GCLEARSCREEN);
  • 769. -pg_dejaultchart(&ent, ~G---LINECHAKF, ~G~OINTANDLlNE); strcpy(en t.main title.title, HRegistros liga de Golf"); -pg_chartms(&ent, meses, (float _far *)valores, EQUIPOS, MESES, MESES, equipos); getch( ); _clearscreen( _GCLEARSCREEN); / * Diagrama de lfneas multiple mostrando solamente dos * columnas de las tres y tres series de las cuatro */ -pg_dejaultchart(&ent, ~G---LINECHAKF, ~~OINTANDLlNE); strcpy(en t.main title.title,HRegistros parciales liga de Golf"); -pg_chartms(&ent, &meses[l], &valores[l][l], EQUIPOS- 1, MESES - 1, MESES, &equipos[lJ); getch( ); exit(L---setvideomode( --.DEFAULTMODE)); } short ~ar _p~chartscatter(chartenv _far * ent, float _far * xvalores, float ~ar * yvalores, short n); _P9_chartscatte rms (e nt,xva I,yva I,n se rieS,n ,CO Ium nas, eti_series)
  • 770. short _far _p~chartscatterms(chartenv ~ar * ent, float _far * xva- lores, float _far * yvalores, short nseries, short n, short columnas, char * _far * eti~eries); eti_series array de etiquetas correspondientes a cada una de las series re- presentadas. # include <conio.h > # include <graph.h > # include <string.h > # include <std/ib.h> # include <pgchart.h> # define VALORES 5 # define SERIES 2 float _far empleados[SERIES][VALORES] { {235, 423, 596, 729, 963 }, { 285, 392, 634, 801, 895 } }; float _far beneficios[SERIES][VALORES] = { {0.9, 2.3, 5.4, 8.0, 9.3 }, { 4.2, 3.4, 3.6, 2.9, 2.7 } }; char ~ar ~mpresas[SERIES] = { "Industrias FJC': "Construcciones C"};
  • 771. main( ) [ chartenv ent; / * Modo grdfico de mds alta resolucion */ if (L...setvideomode( --.MAXRESMODE)) exit(l); / * Grdficos no disponibles */ -pg_dejaultchart (&ent,---.PG~CATTERCHAKI; ---.PG-YOINTONLY},o strcpy(en t.main title.title, "Industrias FIC"); strcpy(ent.xaxis.axistitle. title, "Empleados' '); strcpy(ent.yaxis.axistitle.title, "Beneficios' '); / * La siguiente, son datos para formar la escala del eje x */ ent.xaxis.scalefactor=1000; / *factor de escala:x10, ... */ strcpy(en t.xaxis.scaletitle. title,"x 1000"); ent.xaxis.autoscale=O; /* l=escala automdtica, O=usuario */ ent.xaxis.scalemin = 0.0; / * lImite inferior */ ent.xaxis.scalemax = J.O; / * lImite superior */ ent.xaxis.ticinterval = 0.1; / * intervalo */ ent.xaxis.ticfClrmat=l; / *formato (0 0 1) */ ent.xaxis.ticdecimals=l; / * mimero de decimales */ -pg_chartscatter(&ent, empleados[O], beneficios[O], VALORES); getch( ); _clearscreen( _GCLEARSCREEN); -p~dejaultchart (&ent,---.PG~CATTERCHAKF, ---.PG-YOINTONLY},o strcpy(ent.xaxis.axistitle. title, "Empleados' '); strcpy(ent.yaxis.axistitle.title, "Beneficios' '); / * Lo siguiente, son datos para formar la escala del eje x */ ent.xaxis.scalefactor=1000; / *factor de escala:x10, ... */ strcpy(en t.xaxis.scaletitle. title,"x 1000");
  • 772. ent.xaxis.autoscale = 0; ent.xaxis.scalemin = 0.0; ent.xaxis.scalemax = 1.0; ent.xaxis.ticinterval = 0.1; ent.xaxis.ticformat = 1; ent.xaxis.ticdecimals = 1; / * 1= escala automdtica, 0= usuario */ / * limite inferior */ / * limite superior */ / * intervalo */ / *formato (0 0 1) */ / * mimero de decimales */ -pg_~hartscatterms(&ent, (float _far *)empleados, (float _far *)beneficios, SERIES, VALORES, VALORES, empresas); exit(L ...setvideomode( ~EFA ULTMODE)); 1 Un font es un tipo determinado de letra, como Courier 0 Roman, que pue- de escribirse en varios tamafios. Por ejempl0, "Courier 15 x 12" indica texto donde cada canicter es de tipo Courier y ocupa un area de pantalla igual a 15 puntos verticales por 12 puntos horizontales. Los datos para poder configurar 10s distintos tipos de letras se encuen- tran en 10s ficheros que tienen extensi6n .FON. El nombre del fichero indi- ca el tipo de letra. Para visualizar un texto con un determinado tipo de letra, realizar 10s siguientes pasos: 1. Registrar 10s fonts disponibles (* .FON), en una lista en memoria, mediante la funci6n -,"egisterfonts( ). 2. Llamar a la funci6n -..Setfont( ) para seleccionar un determinado tipo de letra. 3. Situarse en la posici6n deseada de la pantalla con la funci6n _moveto( ) y visualizar el texto utilizando la funci6n _outgtext( ).
  • 773. Lee la informaci6n de cabecera de los ficheros .FON especificados y cons- truye una lista cuya finalidad es dar informaci6n de los ficheros .FON dis- ponibles. Esta funci6n devuelve como resultado el numero de fonts registrados en la lista en memoria 0 un valor negativo si ocurre un error. Busca un tipo de letra (font) que coincida con el conjunto de caracteristi- cas especificado y hace que este sea e1tipo de letra actual. indica el nombre del font elegido, el cual puede ser: cou- rier, helv, tms rmn, modern, script 0 roman.
  • 774. si un font del tamafi0 especificado no esta registrado, se selecciona el font mas apropiado de los registrados. Si al menos hay un font registrado, se utiliza. Si esta opci6n no se especifica y el font elegido no coincide exactamente, ocu- rre un error. selecciona el font numero x, donde x es menor 0 igual que el valor devuelto por la funci6n _registerfonts( ). Esta funci6n devuelve el valor 0 si se ejecuta satisfactoriamente y -1 en caso contrario. Microsoft C utiliza dos metodos para crear tipos de letras (bit-mapping y vector-mapping). La primera tecnica genera los tipos Courier, Helv y Tms Rmn a traves de map as de bits, esto es cada bit en el mapa se corresponde con un pixel de la pantalla. La segunda tecnica genera los tipos Modern, Script y Roman a traves de un mapa de vectores, representando cad a ca- racter en terminos de lineas y arcos. Tipo Mapa Tamafio en pixels Espaciado courier bit 10 x 8, 12 x 9, 15 x 12 fijo helv bit 10 x 5, 12 x 7, 15 x 8 proporcional 18 x 9, 22 x 12, 28 x 16 tms rmn bit 10 x 5, 12 x 6, 15 x 8 proporcional 16 x 9, 20 x 12, 26 x 16 modern vector a escala proporcional script vector a escala proporcional roman vector a escala proporcional
  • 775. Devue1veel ancho que se requiere para escribir con la funcion _outgtext( ) el texto en e1tipo de letra actual. Esta funcion devuelve e1ancho en pixels del texto a visualizar, 0 un -1 si e1font no existe. Esta funcion devuelve en una estructura de tipo -fontinjo, las carac- teristicas del font actual. struct ~ontinfo { int int int int int char char }; type; ascent; pixwidth; pixheight; avgwidth; filename[81]; facename[32]; / * metodo (bit/vector) para crear letras */ / *pixels desde la cima hasta la base */ / * ancho del cardcter en pixels */ / * alto del cardcter en pixels */ / * anchura media de los caracteres */ / * nombre del jichero incluyendo camino */ / * nombre del tipo de letra (font) */ Visualiza el texto sobre la pantalla en el tipo de letra actual y en la posicion definida por -setgtextvector( ).
  • 776. ( 0, 0) ( 1, 0) ( 0, 1) (-1, 0) ( 0,-1) no cambia. " texto horizontal (por defecto). rota 90 grados en sentido contrario alas agujas del reloj. rota 180grados. rota 270 grados en sentido contrario alas agujas del reloj. # include <conio.h > # include <stdio.h> # include <stdlib.h> # include <string.h> # include <graph.h > unsigned char *textos[NFUENTESj { «Courier': «Helvetica': «Times Roman': «modern': «Script': «Roman" };
  • 777. unsigned char *tipos[NFUENTESj = { int main(void) { unsigned char lista[20j; char dir-font[~AX----.PATHj; / *directorio donde estan los FONTS */ struct videoconfig cv; / * configuracion de v{deo */ struct -fontinfo info-font; / * informacion sobre los FONTS */ short nfont, x, y; , / * Inicializar el sistema grafico para tipos de letras. * Leer la informacion de cabecera de todos los .FON */ if (_registerfonts( fC*.FON" ) < = 0) [ puts(fCEscribir el camino completo para los ficheros *.FON·"); gets(dir-font); / *p. e.: C: C600 SOURCE SAMPLES */ strcat(dir -font, fC *.FON"); if (_registerfonts(dir -font) < = 0) [ putst'Error: *.FON no pueden ser cargados"); "exit(l); l l / * Modo grafico de mas alta resolucion */ if (!----.Setvideomode(~AXRESMODE)) exit(l); / * Graficos no disponibles */ / * Poner la configuracion en cv */ ---$etvideoconfig( &cv); / * Visualizar cada tipo de letra centrado en la pantalla */ for (nfont = 0; nfont < NFUENTES; nfont+ +) { / * Construir cadena de tipos */ strcat(strcat(strcpy(lista, fC t' "), tipos[nfontJ), fC , "); strcat(lista, fCh30w24b");
  • 778. _clearscreen( _GCLEARSCREEN); if (~etfont(lista) > = 0) { if (---$etfontinfo(&info-font)) { _outtext(HError: No se puede cargar informacion "); break; } 1* Centrar el texto en funcion de su longitud *1 x = (cv.numxpixelsI2) - (---$etgtextextent(textosfnfontJ)12); y = (cv.numypixelsI2) + (---$etgtextextent(textosfnfontJ)12); ~oveto(x, y); if (cv.numcolors > 2) ~etcolor(nfont + 1); 1* Rotar y visualizar el texto *1 --setgtextvector(L ~; _outgtext(textosfnfont J); ~etgtextvector( 0, 1); _outgtext(textosfnfont J); --setgtextvector( -1, 0); _outgtext(textosfnfont J); ~etgtextvector( 0, -1 ); _outgtext(textosfnfontJ); getch( ); } else _outtext(HError: font no encontrado"); } _unregisterfonts( ); exit(!~etvideomode( --.DEFAULTMODE)); }
  • 779. PARTE 7 Entorno Integrado de Desarrollo • Utilizaci6n del PWB • Instalaci6n de Microsoft C
  • 780. PWB (Programmer's WorkBench) es un entorno de programaci6n basado en ventanas, que incorpora un editor de textos, un compilador, un enlaza- dor, un depurador, la utilidad Make, un analizador de c6digo fuente y un sistema de ayuda en linea. sfr representa una serie de 6rdenes que seran ejecutadas en el momenta de arrancar PWB. Impide tanto las inicializaciones como la lista de los ficheros ultimamente accedidos.
  • 781. Cuando se entra en el entorno de programaci6n, mediante la orden PWB, 10 primero que aparece es el menu principal y la ventana de edici6n. Las partes que componen la pantalla del PWB se detallan a continuaci6n. I. : • Help: fseek "Description. "ExaMpl~ -4lJ~<ontents. "Inde~ "Back. Syntax: int fseek( FILE wstreaM, long offset, int origin ). origin: SEEK_CUR. SEEK_END. SEEK_SET II ••• ·S~I~'S"'II;J·qa;HI§~'•• fseek(pf, desp, SEEK_SET); fread(areg, bytesreg, 1. pf); printf("NoMbre: %s'n", reg.noMbre); printf("Nota: %d'n'n", reg.nota); } /w Si se ha pulsado una tecla no valida w/ if (!c) fflush(stdin); Es la primera linea; en ella se visualizan los nombres de los menus dispo- nibles.
  • 782. Aparece en la esquina superior izquierda de la ventana, cuando hay mas de una ventana abierta. Sirve para cerrar la ventana utilizando el raton. Aparece en la esquina superior derecha de la ventana, cuando hay mas de una ventana abierta. Sirve para ampliar al maximo la ventana por media del raton. Cada ventana activa visualiza dos barras de scroll para utilizar con el ra- ton; la barra de scroll vertical esta situada a la derecha de la ventana y la barra de scroll horizontal esta situada en el fondo de la ventana. En la parte superior de cad a ventana aparece el nombre del fichero con el que estamos trabajando. Es la ultima linea de la pantalla de PWB. Visualiza diversos tipos de infor- macion: Teclas titiles. FI = Ayuda, Alt = Menu, F6 = Cambiar de ventana. Indicador de tipo de fichero. C: fichero fuente C text: cualquier otro fichero creado por el usuario pseudo: pseudofichero (area de memoria que nunca es escrita so- bre el disco) Indicadores de estado. C: mayusculas activadas L: no se utiliza el CR para finalizar una linea
  • 783. M: el fichero ha sido modificado N: teclado numerico activado 0: modo sobreescritura R: fichero para solo leer T: fichero temporal X: se esta registrando una macro El menu principal consta de nueve opciones: File, Edit, View, Search, Make, Run, Options, Browse y Help. Para seleccionar una de las opciones pre- sentadas, se puede optar por cualquiera de las dos formas siguientes: • Pulsar la tecla Alt, para activar el menu principal, y despues la te- cla correspondiente a la letra que se presenta en alto brillo 0 color diferente (es indiferente el utilizar mayusculas 0 minusculas). Por ejemplo F para activar el menu correspondiente a la opcion File. • Pulsar la tecla AIt, para activar el menu principal, y utilizando las teclas de movimiento del cursor, elegir la opcion deseada (opcion que se presentara en video invertido respecto al existente 0 en color diferente) y pulsar la tecla Enter. Para seleccionar una orden correspondiente al menu presentado por una opcion del menu principal, se puede proceder de cualquiera de las dos formas siguientes: • Pulsar la tecla correspondiente a la letra que se presenta en alto bri- 110 0 color diferente (es indiferente el utilizar mayusculas 0 minus- culas). Por ejemplo, si se esta en el menu presentado por la opcion File y se qui ere salir del PWB, se pulsa la tecla x. • Moverse a la orden deseada por medio de las teclas de movimiento del cursor y pulsar Enter. Algunas ordenes de estos menus tienen escrito a su derecha el nombre de una tecla 0 combinacion de teclas que realizan la misma operacion. Por ejemplo la cuarta orden del menu presentado por la opcion Edit es
  • 784. "Cut Shift + Del". Esto significa que al pulsar las teclas Shift + Del se ejecuta la orden Cut. Algunas ordenes abren una ventana de dialogo; por ejemplo, la orden "Find ..." del menu presentado por la opcion Search. En este caso respon- deremos alas cuestiones planteadas, y finalizaremos pulsando a continua- cion Enter « OK ». Siempre que se desee abandonar un menu, se pulsara la tecla Esc (<Cancel> ). Para obtener ayuda sobre cualquier orden, seleccionarla y pulsar Fl. Para abandonar la pantalla de ayuda, pulsar la tecla Esc. PWB esta disefiado para utilizar un raton de Microsoft 0 uno compatible con este. 1. Se apunta a la opcion deseada del menu y se pulsa el boton iz- quierdo del raton. Para enrollar/desenrollar el texto sobre la ventana activa, se dispone de una barra de scroll vertical y otra de scroll horizontal. Ambas tienen en sus extremos un as flechas que indican el desplazamiento imaginario de la pantalla sobre el texto cuando se efectua la accion de scroll y un cursor que se desplaza a 10 largo de la linea de scroll, que indica la posicion relati- va del cursor de pantalla con respecto a los limites del texto. 1. Para avanzar 0 retroceder una linea, se apunta a la flecha supe- rior 0 inferior de la barra de scroll vertical y se pulsa el boton iz- quierdo del raton. Para desplazar el texto una posicion a la izquierda o a la derecha se apunta a la flecha derecha 0 izquierda de la ba- rra de scroll horizontal y se pulsa el boton izquierdo del raton.
  • 785. 2. Para avanzar 0 retroceder una pagina, se coloca el cursor del ra- t6n sobre la linea de scroll vertical, entre el cursor de scroll y la flecha inferior 0 entre el cursor de scroll y la flecha superior, y se pulsa el bot6n izquierdo del rat6n. La operaci6n es analoga para desplazar el texto una pagina hacia la izquierda 0 hacia la dere- cha; eso sf, actuando sobre la barra de scroll horizontal. 3. Si se apunta al cursor de scroll vertical de la ventana de texto y se tira, con el bot6n izquierdo del rat6n pulsado, hacia arriba 0 hacia abajo arrastrandolo sobre la linea de scroll, el texto se des- plaza hacia abajo 0 hacia arriba. La acci6n se ve cuando se deja de pulsar el bot6n. Esta misma operaci6n se puede realizar sobre la linea de scroll horizontal para desplazar el texto hacia la izquieraa o hacia la derecha. Es posible variar el tamafio de una ventana. Para ello, se apunta a la linea de separaci6n entre ventanas y con el bot6n izquierdo del rat6n pul- sado, se tira en la direcci6n apropiada para agrandar 0 reducir la ventana. Para activar una ventana, apuntar a cualquier lugar dentro de la mis- ma y pulsar el bot6n izquierdo del rat6n. Para activar 0 desactivar cualquier acci6n dentro de una ventana de dialogo, apuntar al espacio entre corchetes 0 entre angulos y pulsar el bo- t6n izquierdo del rat6n. Al ejecutar las 6rdenes seguidas por tres puntos (...) de los menus citados anteriormente, se presenta una ventana denominada ventana de dhilogo, que puede contener cuestiones para responder y opciones para elegir. Para pasar de una cuesti6n u opci6n a otra, pulsar la tecla Tab. Una vez situa- dos, podremos elegir 0 eliminar una opci6n con las teclas de movimiento del cursor. Tambien, y mas faci!, pulsando la tecla correspondiente a la le- tra en alto brillo de cualquiera de las opciones, activamos 0 desactivamos dicha opci6n, 0 bien, nos situamos en la correspondiente cuesti6n a res- ponder.
  • 786. Desde este menu, entre otras cosas, se pueden crear nuevos ficheros, car- gar ficheros ya existentes, fusionar ficheros, salvar ficheros, visualizar el siguiente fichero en la lista, renombrar un fichero, escribir todo 0 parte de un fichero 0 salir al DOS. Search Make Run Options ,---------------,- c' ,Cf,m"S ~ Open . Merge . Next Save Save As ... Save All Close Print ... DOS Shell Shift+F9 {53.1. 41.8. 19.5. 13.7. 10.8. 20.o}; S] = ...Greeia .....Tunieia .....Turqul.a.....Otros ..}; ] = {O. 1. O. O. O. 0 };~xi t I'llt+F4 1 PROG03.C All+l 2 PROG02.C Alt+Z 3 <UNTITLED> Alt+3 ~ ~I pen a New EMptlj File C N 00001.001 /M Modo grafieo de MaS alta resoluei6n M/ if (!_setvideoMode(_MAXRESMODE» exit( 1 ); /M Grafieos no disponibles M/ Crea un nuevo fichero denominado UNTITLED en memoria. Cuando sal- vemos el fichero en el disco, prodremos asignarle un nuevo nombre. Carga en memoria un fichero existente en el disco. El fichero puede estar en el directorio actual de trabajo, 0 en otro directorio. Para especificar el fichero que se desea cargar, se puede proceder de cualquiera de las formas siguientes:
  • 787. Make Run Options- Brow C·,Cb00,SOURCE,PROG01.[ 1. Escribir e1nombre del fichero a continuacion de File Name y pul- sar Enter. 2. Pulsar la tecla Tab para situar el cursor sobre la lista de ficheros (File List) y elegir el deseado, bien con las teclas de movimiento del cursor, 0 bien pulsando la inicial del nombre una 0 mas veces, ya que puede haber iniciales repetidas. A continuacion pulsar Enter. 3. Apuntar con el raton al nombre del fichero y pulsar una vez el boton izquierdo del raton para seleccionarlo 0 dos vcces consecu- tivas para cargarlo. Para cambiar de directorio, seleccionar uno de los de la lista Drives / Dirs: (aparecen en letras mayusculas) y pulsar Enter; 0 bien, escribir a continuacion de la pregunta File Name: el camino del directorio al que se desea cambiar y pulsar Enter. ( 0/ > <Cancel> ( Help >0 • ~I Fl=Help Enter E<.c C,lnn>! T.b Ne,t FIPlrl [" N 00001.001 ,.>< if (! setvideoMode( MAXRESMODE» exit( 1 ); - ,.>< Graficos no disponibles ><,. Inserta el contenido de otro fichero, encima de la linea sobre la que se en- cuentra el cursor.
  • 788. Cargar el siguiente fichero de la lista de ficheros, 0 historia, visualizada al final del menu File. El fichero en memoria se descarga de la misma y su nombre se afiade a esta lista. Escribe el contenido del modulo que actualmente reside en memoria, en un fichero en el disco. Escribe en el disco el contenido del modulo que actualmente reside en me- moria, con el nuevo nombre especificado. Cierra el fichero actual mente en memoria y 10 elimina de la lista 0 historia de ficheros. Si el fichero nunca ha sido salvado 0 ha sido modificado, se nos preguntara si queremos salvarlo. Permite escribir por la impresora, todo el contenido del fichero que se esta editando, 0 solamente el texto seleccionado.
  • 789. Uinclude <conio.h> uinclude <stdlib.h> Uinclude <graph.h> uinclude <string.h> lIinclude udefine float f char _fa { short _f < OK ) <Cancel> < Help> . ~ f1=Hfdp Enter E,;c=Canccl Tab=Next field C 0000l.mll /N Modo grafico de MaS alta resoluci6n N/ if (! setvideoMode( MAXRESMODE» exit( 1 ); - /N Graficos no disponibles N/ Permite salir temporalmente al DOS, pudiendo asi ejecutar cualquier otra tarea bajo el sistema operativo. La vuelta al PWB se hace ejecutando la orden Exit. Si el fichero actual ha sido modificado es automaticamente salvado. Finaliza la sesi6n con Microsoft C (PWB) y nos devuelve al DOS. Si al eje- cutar esta orden el fichero actual ha sido modificado, es automaticamente salvado. Las caracteristicas fundamentales del editor perteneciente al entorno de pro- gramaci6n, son las siguientes:
  • 790. • Movimiento del cursor a cualquier parte de la pantalla, para modi- ficar 0 insertar texto. • Manipulaci6n de bloques; esto es, mover, duplicar 0 borrar un blo- que de texto. Muchas de las 6rdenes que se exponen a continuaci6n requieren la selec- ci6n previa de un conjunto de caracteres, palabras 0 lineas. Para realizar esta operaci6n dentro de la ventana activa, colocar el cursor al principio del texto a seleccionar y manteniendo pulsada la tecla Shift, marcar el tex- to desplazando el cursor con las teclas de movimiento. Tambien es posible seleccionar texto con el rat6n. Para ello, proceder de la forma siguiente: Apuntar al canicter deseado y con el bot6n iz- quierdo del rat6n puisado, tirar hacia la dere- cha (0 izquierda) para seleccionar el canicter 0 caracteres deseados. Afmntar a la palabra y pulsar dos veces conse- cutivas el bot6n izquierdo del rat6n. Apuntar a la primera columna de la linea y con el bot6n izquierdo pulsado tirar hacia abajo has- ta seleccionar las lineas deseadas.
  • 791. EI editor utiliza varias ordenes para realizar las operaciones antes descri- tas. Estas ordenes se pueden agrupar de la forma siguiente: Ctrl+S 0 Ctrl+D 0 Ctrl+A 0 Ctrl+F 0 Ctrl+E 0 Ctrl+X 0 Ctrl+ Ctrl+ t ! Home End Ctrl+Home Ctrl+End Un canicter a la izquierda Un canicter a la derecha Al principio de la palabra a la izda. Al principio de la palabra a la dcha. Una linea hacia arriba Una linea hacia abajo Al primer nivel de dentacion Al final de la linea actual A la primera linea del programa A la ultima linea del programa
  • 792. Scroll Ctrl+W 0 1 Ctrl+Z 0 I Ctrl+R 0 PgUp Ctrl+C 0 PgDn Insertar Scroll una linea hacia arriba Scroll una linea hacia abajo Scroll una pagina hacia arriba Scroll una pagina hacia abajo Ctrl+V 0 Ins End Enter Ctrl + N Home Enter Shift + Ins Ctrl + P + caracter Activar/desactivar inserci6n Linea debajo de la actual Linea encima de la actual Contenido de la memoria intermedia Introduce el caracter equivalente a Acaracter Ctrl+G 0 Bksp (-) Del El caracter a la izda. del cursor El caracter bajo el cursor La linea actual, salvandola en la memo- ria intermedia El texto seleccionado, salvandolo en la memoria intermedia Shift + El caracter a la izda. Shift + El caracter a la dcha. Shift + La linea de encima Shift + I La linea actual Shift + Ctrl + La palabra a la izda. Shift + Ctrl + La palabra a la dcha. Shift + PgUp La pantalla por encima del cursor Shift + PgDn La pantalla por debaj 0 del cursor Shift + Ctrl + Home Hasta el principio del programa Shift + Ctrl + End Hasta el final del programa
  • 793. FI Ayuda F2 Cargar ultimo fichero F3 Repetir la busqueda hacia adelante F4 Repetir la busqueda hacia atrcis F6 Siguiente ventana F7 Ejecutar hasta la posicion actual del cursor F8 Ejecucion paso a paso, incluyendo funciones de usuario F9 Poner/quitar punto de parada FlO Ejecucion paso a paso, excepto para las funciones de usuario Cuando se selecciona la opcion Edit del menu principal para su ejecucion, se presenta en pantalla un menu con las siguientes ordenes: - Edit I. I Cut ~hIft·DP! COPlj I:trl-In', P,,<:t P. <;h 1 ft· In', Cl p.le fl,'1 Set Anchor Selecl To Anchor . " Unclo the 11::l co III ny r Or1r11rln IY.( lltin N UUUUl WJl
  • 794. Esta orden se ejecuta despues de Undo. Cuando se ejecuta Redo el fichero vuelve a la forma que tenia antes de ejecutar la ultima orden Undo. Esta orden borra el texto seleccionado y 10 salva en una memoria interme- dia (ver 6rdenes para seleccionar texto). Copia en la ventana activa el texto almacenado en la memoria intermedia. Podemos realizar las dos operaciones siguientes: Reemplazar texto: seleccionar el texto que deseamos reemplazar en la ventana activa y ejecutar Paste. Insertar texto: mover el cursor a la posici6n deseada dentro de la ven- tana activa y ejecutar Paste. El textose inserta a continuaci6n del cursor.
  • 795. Esta orden borra el texto se1eccionado y no 10 salva en la memoria in- termedia. Salva la posicion actual del cursor como una marca (similar a la orden AKB de WordStar). Mas tarde, esta marca puede utilizarse como punto final en la seleccion de texto. Selecciona el texto que hay entre la posicion actual del cursor y la marca salvada por la orden Set Anchor. Permite alternar entre los modos: caja, linea y flujo. En modo caja, se puede seleccionar una region rectangular de texto. En modo linea, se pueden se- leccionar solo lineas. En modo flujo, la seleccion se hace siguiendo el texto ASCII. Coloca el editor en modo de lectura solamente con 10 que el texto queda pro~egido contra posibles cambios. Permite definir el nombre para una macro y las teclas para referirse a ella. Para asignar las teclas, mover el cursor a [ J y pulsar la tecla 0 combinaci6n de teclas deseada.
  • 796. Ejecutar Record On para empezar a registrar una macro, y al finalizar el registro de la misma. Permite cambiar una macro existente. Para modificar una macro que aca- bamos de registrar: 1. Cargar el pseudofichero que contiene la macro, ejecutando la or- den Edit Macro. Bloques de texto pueden ser movidos 0 copiados con las 6rdenes Cut, Copy y Paste. Para ello seguir los pasos que se indican a continuaci6n: 2. Para realizar la operaci6n de mover, ejecutar la orden Cut del menu Edit.
  • 797. Para realizar la operacion de copiar, ejecutar la orden Copy del menu Edit. En ambos casos, el texto seleccionado es salvado en una memoria intermedia. 3. Mover el cursor allugar donde se quiere insertar el texto. Este lu- gar puede estar en: • otro fichero. Utilizar la orden Open para cargar el fichero en memoria 0 seleccionarlo de la historia. Cuando se elige la opcion View del menu principal se presenta un menu con las siguientes opciones: Search Make Run Optiqns r---------------,EO' ~ Spl it Vertical Size Window' MaxiMize Window Close Window Ctrl +F8 Ctrl+F10 Ctrl+F4' COMpil e Resul ts ~ ~ 'lplll Winnow llo('lzonLlllq dt (IIr·~.nr' '" PlllllI N WllHll rHlt
  • 798. Divid~ la ventana activa de izquierda a derecha. La division se hace por la posicion del cursor. EI minimo tamafio es de 5 lineas. Divide la ventana activa de arriba a abajo. La division se hace por la posi- cion del cursor. EI minima tamafio es de 10 columnas. Permite agrandar 0 acortar la ventana activa. Para utilizar esta orden debe haber al menos dos ventanas abiertas. Cierra la ventana activa. Para utilizar esta orden debe haber al menos dos ventanas abiertas. Abre y cierra la ventana de errores durante la compilacion. Para localizar la linea del program a donde se ha producido un error: 2. pulsar Shift + F3 para moverse al siguiente error 0 Shift +F4 para moverse al error anterior. EI cursor se situani sobre la linea del programa que causo el error.
  • 799. Este menu per mite encontrar en un programa cualquier texto especificado y opcionalmente reemplazarlo por otro texto. Consta de las siguientes 6rdenes: ~ Se Iected Text f3 Repeat Last flnd f3 Change . for FIle . Next Error Shlft·f3 PrevIous Error Shlft+F4 Set Error Go To Mark . DefIne Mark . Set Mark fIle . • .1 f Incl c;lCllly UI' l'p~Juld(~ P"PI·f'~.~iun f'<".f-'udo N nnnOl.nnl Permite buscar un canicter, una palabra 0 un grupo de palabras. Cuando esta orden se ejecuta aparece una pantalla de dialogo. Para encontrar un texto determinado, realizar los siguientes pasos: 1. Introducir (seleccionandolo 0 escribiendolo) el texto a buscar a continuaci6n de la pregunta Find What:. C Tener en cuenta mayusculas y minusculas. R Expresiones regulares (busqueda por patrones).
  • 800. W Busca desde el cursor hasta el final del fichero y desde el final del fichero hasta el cursor. o Hacia adelante (Forward). B Hacia atnis (Backward). A Pone en alto brillo todas las ocurrencias encontradas. Si no se selecciona se para en la primera ocurrencia. (Find All). Cuando se requiere utilizar expresiones regulares, pueden utilizarse como como dines los siguientes caracteres: la localizaci6n del texto escrito despues de este caracter debe darse al principio de una linea. la localizaci6n del texto escrito antes de este caracter debe darse al final de una linea. localizar uno de los caracteres del conjunto especificado entre cor- chetes. Dentro de los corchetes pueden utilizarse los caracteres espe- ciales: para indicar los limites del conjunto de caracteres que se desea especificar. cualquier caracter de los indicados precedido por este caracter, pier- de su significado especial y es considerado como un caracter normal.
  • 801. Esta orden permite buscar un texto seleccionado previamente. Los pasos a seguir son los siguientes: 1. Seleccionar el texto que se quiere buscar (ver 6rdenes del editor para seleccionar). El texto seleccionado debe de estar sobre una unica linea. Tanto utilizando Find como Selected Text se puede repetir la ultima busqueda, pulsando F3 0 ejecutando la orden Repeat Last Find de este mis- mo menu. Esta orden permite repetir la ultima busqueda realizada por Find 0 por Selected Text. La busqueda puede realizarse hacia adelante (F3) 0 hacia atnis (F4).
  • 802. 1. Introducir (selecciomindolo 0 escribiendolo) el texto a buscar a continuaci6n de la pregunta Find What:. 2. Introducir el texto que va a sustituir al anterior a continuaci6n de la pregunta Change to:. C Tener en cuenta mayusculas y minusculas. R Expresiones regulares (busqueda por patrones). W Busca desde el cursor hasta el final del fichero y desde el final del fichero hasta el cursor. < Find and Verify>. Confirmar cada cambio. <Change All>. Efectuar todos los cambios sin confirmar. <Cancel>. Cancelar la orden. "include (c lIinclude , lIinclude 'g lIinclude , lIinclude < lIdefine PAl float far char far - {"It short _far explotar[PAISESl ~M Modo grafico de Mas alta resolucibn M~ if (! setvideoMode( MAXRESMODE» exit( 1 ); - ~M Graficos nO disponibles M~ ~ 4 F1=Help Enter Esc=C"ncel T~b=Next FIeld C 00001.001
  • 803. Encontrar un fichero sobre el disco. La busqueda puede realizarse sobre un directorio especificado 0 a 10 largo de la estructura en arbol de los di- rectorios y partiendo de uno determinado. Mueve el cursor a la linea fuente que contiene el siguiente error que se ha producido en la compilaci6n. Mueve el cursor a la linea fuente que contiene el error inmediatamente an- terior al error de compilaci6n que actual mente se esta visualizando. Selecciona como error actual el error que esta bajo el cursor. Esta orden sincroniza las ventanas fuente y la de errores, para que la linea fuente que contiene el error aparezca en la ventana activa. La orden Set Error esta disponible si el cursor esta en la ventana de errores de compilaci6n. En otro caso, no tiene efecto. Mueve el cursor a una marca definida con la orden Define Mark. Una marca es un nombre asociado con una posici6n (fila y columna) en un fichero.
  • 804. Hay dos formas para afiadir el contenido total 0 parcial de otros ficheros al fichero actual que se esta editando. Para copiar un fichero entero dentro del texto actual, se utiliza la orden Merge del menu File. El nuevo texto se inserta encima de la linea sobre la que esta el cursor. Para copiar parte de un fichero dentro del texto actual, realizar los siguien- tes pasos: 1. Ejecutar la orden Open ... del menu File para cargar el fichero que contiene el texto que queremos copiar, 0 recuperar el fichero de la historia. 3. Ejecutar una de las 6rdenes Cut 0 Copy del menu Edit, para sal- var el texto seleccionado en la memoria intermedia. 4. Ejecutar la orden Open ... del menu File para cargar el fichero en el cual queremos copiar el texto seleccionado, 0 recuperar el fi- chero de la historia y situar el cursor en el lugar adecuado. 5. Ejecutar la orden Paste del menu Edit. El texto se inserta a conti- nuaci6n del cursor.
  • 805. Copiando texto correspondiente al manual de ayuda electr6nico 4. Pasar a la ventana de edici6n y depositar el texto copiado en el lugar deseado utilizando la orden Paste. Un programa C consiste de uno 0 mas ficheros fuente conocidos como modulos. Los programas de un unico m6dulo son los mas faciles de crear. Los pasos a seguir para esto son los siguientes: 3. Compilar, ejecutar y depurar el programa a traves de los menus Run y Options. Para mantener un programa farmado por varios m6dulos, se puede crear una Iista con los nombres de los mismos, mediante la orden Set Pro- gram List ... del menu Make, y a continuaci6n se procede a la compilacion de esta lista. Esta orden se estudia a continuaci6n. Esta lista sera utilizada par Microsoft C para reconstruir el programa, cuando cualquiera de sus m6dulos haya sido modificado. Otra posible soluci6n, aunque no tan co- moda, seria construir una libreria, cuesti6n que fue vista en el capitulo co- rrespondiente a "Librerias y utilidades del compilador".
  • 806. Cuando se dige la opci6n Make del menu principal, se presenta un menu con las siguientes opciones: uinclude <conio.h> Uinclude <stdlib.h> Uinclude <graph.h> Uinclude <string.h> Uinclude <pgchart.h> Set PrograM LIst ... Edit PrograM Llst... PROG Clear PrograM Ll~t Udefine PAlSES & float _far valor[PAISESl = {53.1. 41.8. 19.5. 13.7. 1a.8. za.&}; char far HeleMentos[PAISESl = - {..Italia .....Espana .....Grecia .....Tunicia .....l'urquia..•..Otros"}; short _far expiotar[PAISESJ = {a. 1. a. a. a. a }; /H Modo grafico de Mas alta resoluci6n H/ if (! setvideoMode( MAXRESMODE» eXit( 1 ); - /H Graficos no disponibles */ ~ ~ COMpIle current source fIle C 00003.059 Esta orden crea un fichero objeto (.obj) del m6dulo actual, pero no efec- tua e1enlace (link). Esta orden compila y enlaza todos los m6dulos pertenecientes al programa actual que hayan sido modificados desde la ultima vez que se compilaron y enlazaron, y crea un unico fichero ejecutable (.exe). Los programas for- mados por varios m6dulos requieren de una lista (program list).
  • 807. Esta orden compila y enlaza todos los modulos pertenecientes al programa actual (program list), creando un unico fichero ejecutable (.exe). Esta orden crea una nueva lista de modulos para un programa 0 carga una lista ya existente. Antes de proceder como se indica a continuacion, elegir el tipo de fi- chero que se desea construir. Para ello ejecutar la orden Build Options del menu Options. De la ventana de dialogo presentada, elegir la opcion Set Initial Build Options, la cual nos permitira elegir el tipo de fichero que deseamos construir; por ejemplo, DOS EXE. ( OK ) <Cancel> < Help> Para crear un programa con multiples modulos, seguir los pasos que se indican a continuacion:
  • 808. 1. Crear los ficheros fuente que van a formar el programa, siguien- do para cada uno de ellos los puntos que a continuacion se indican: a) Ejecutar la orden New del menu presentado par File para crear un nuevo fichero fuente, 0 ejecutar la orden Open de este mis- mo menu para editar un fichero que ya existe. 2. Ejecutar la orden Set Program List del menu presentado por File, para crear la lista del programa 0 para cargar una ya existente. 3. Ejecutar la orden Edit Program List del mismo menu, para afia- dir y/o eliminar modulos de la lista (Add/Delete) y salvar la mis- ma (SaveList). Es posible tambien, afiadir ficheros objeto y libre- rias .LIB a la lista. 5. Finalmente, compilar, ejecutar y depurar el programa formado por esa lista de modulos. Cuando se compila un programa farmado por multiples modulos, Mi- crosoft C verifica la lista del programa para ver si algun modulo ha cam- biado desde la ultima vez que el programa fue compilado. Si es as!, Micro- soft C vuelve a compilar los modulos que han cambiado antes de reconstruir el programa. La lista del programa es guardada en un fichero en el disco, que tiene el mismo nombre del programa y extension .mak. Fuera del entorno de Mi- crosoft C (PWB), el fichero .mak sera la entrada para la utilidad NMAKE. Esta arden permite mantener la lista de modulos que componen un programa.
  • 809. lista de 10s ficheros fuente existentes en e1 directorio especificado. esta opdon afiade e1fichero se1eccionado a 1a lista del programa 0 10 elimina de 1a lista. Si e1fichero no esta en 1a lista 10 afiade y si esta 10 e1imina. salva todos 10s cambios efectuados en 1a lista de mo- du10s que componen e1 programa en un fichero con extension .mak.
  • 810. Cuando se elige la opci6n Run del menu principal, se presenta un menu con las siguientes opciones: Run DOS C ...J!'l'uf,d ... [U=.tC'11=E r-"lenu..• . . [,ecule the current pr0gr"~ p~~IJ~- N OOOQl.nQl Permite introducir argumentos para ser interpretados por el programa cuan- do se ejecute la orden Ejecute 0 la orden Debug. Esta orden es equivalente a introducir los argumentos en la linea de 6rdenes de DOS.
  • 811. Permite ejecutar cualquier orden DOS sin salir del PWB. No ejecutar pro- gramas residentes (TSR), tal como MSHERC.COM. Permite afiadir hasta seis 6rdenes al menu Run. Esta orden nos permite ejecutar un programa desde dentro de PWB sin tener que especificar el nom- bre del programa y sus argumentos cada vez. La orden Customize Menu presenta un menu con las siguientes op- dones:
  • 812. Options • • Iiiiiiiit • - • Rullel Option,; ... Hr/)w~.•e Optiuns ... C LUMpllcr Optlun~ ... LINK OptIons . NMAKE OptIonS . CodeUlew OptIonS ... . ~ ~et envIronMent optIons pseuelo N 00001.001 Define los directorios donde buscar los ficheros de cabecera (.h), las libre- rias (.lib) y los ficheros de ayuda (.hlp) de Microsoft C. Estos caminos de busqueda normalmentte son definidos utilizando variables de entorno del DOS. Permite asignar teclas que ejecutan 6rdenes, macros y funciones. Esta in- formaci6n es almacenada en un pseudofichero (area de memoria que nun- ca es escrita sobre el disco). Permite realizar cambios en el pseudofichero que contiene el conjunto ac- tual de asignaciones hechas para el editor. Ejecutar la orden Save, para sal- var los cambios efectuados en el fichero TOOLS.IN!.
  • 813. Utilizar esta orden para: indicar si el programa va a ser depurado (Debug) o no (Release); seleccionar el directorio destino de los ficheros resultantes de la construccion del programa; seleccionar el lenguaje fuente; seleccio- nar opciones predefinidas para la construccion del programa; salvar el con- junto de opciones una vez actualizadas, para utilizarlas mas tarde. Permite utilizar una base de datos previamente construida, para analizar el codigo fuente de un programa. 1. Generar una lista de modulos utilizando la orden Set Program List del menu Make. Estos son los ficheros que formaran la base de datos. 2. Ejecutar la orden Browse Options del menu Browse y seleccionar la opcion Generate Browse Information. El resto de los campos son opcionales. 3. Construir el programa utilizando las ordenes del menu Make. Este proceso crea un fichero con extension .BSC (Browser Source Ca- che) que contiene informacion para el analizador (Browser). Una vez completado estos pasos, tenemos construida la base de da- tos; por 10 tanto, podemos pasar a utilizar las ordenes del menu Browse (ver menu Browse). Esta orden permite fijar opciones para compilar un programa C. Estas op- ciones son:
  • 814. Numero de segmentos C6digo Datos Small Medium Compact Large Huge 1 1 varios varios varios (arrays> 64K) varios varios Niveles de advertencia 0, 1, 2, 3 y 4. Cuanto mayor es el nivel, mayor es la informaci6n que nos da el com- pilador. Especifica las librerias bajo las cuales estamos tra- bajando.
  • 815. DOS Windows Windows DLL Utiliza por defecto las librerias apropiadas. Librerias para el modo pro- tegido Librerias para el modo real Librerias para windows Para trabajar con librerias para windows enlazadas dimimi- camente Para trabajar con librerias para OS/2 enlazadas dinamicamente libreria para crear una aplica- cion que utiliza multithreads. Deshabilita las extensiones que Microsoft afiade al lenguaje. Permite utilizar las extensiones de Microsoft. Habilita el conjunto de instrucciones del procesador especificado. 8086 80186 80286 /00 /01 /02 Convenio de Hamada Determina el convenio de Hamada a funciones.
  • 816. Fastcall Pascal C /Or /Oc /Od Windows Entry/Exit Code Oenera c6digo para Windows (Ow). Additional Options... Afiadir opciones que no estan en esta pantalla. Set Debug Options... Afiadir opciones cuando un programa se compila para depurarlo. Show Debug Options... Mostrar las opciones utilizadas por defecto, cuando un program a se compila para depurarlo. Set Release Options... Afiadir opciones cuando un programa se compila nor- malmente. Show Release Options... Mostrar las opciones utilizadas por defecto, cuando un programa se compila normalmente.
  • 817. Las 6rdenes de este menu permiten analizar un programa, a traves de sus declaraciones y funciones. ninclude <conio.h> ninclude <stdlib.h> ninclude <graph.h> ninclude <string.h> ninclude <pgchart.h> . . I, .l~olo h'eference. U ip.IJ h'el::"ti()n~-:hip. Li"t. Hpfprpncl-'~;. [all Tree. , Outline .. Next [trl·NllMo Previou::::; Cll~l'Nun .[""", S~n~;i l i Vp Spl it. WiIldow~.; ndefine PAISES D float far valor[PAISES] = {53.1. 41.8. 19 char far MeleMBntos[PAISES] = - {..Italia .....Espaila.....Grecia .....Tunici short _far explotar[PAISESJ = {B. 1. B, +-- ~ tl 'nd deflnll.l0n(sl of SLJMbol [ 00004.0/4 ~M Modo grafico de MdS alta resoluci6n M/ if (! setvideoMode( MAXHESMODE» exit( 1 ); - /M Graficos no disponibles M/ Visualiza una lista de ficheros y numeros de linea donde se ha echo refe- rencia a los simbolos utilizados en el programa. Con esta orden podemos encontrar la referencia para cualquier funci6n, variable, tipo 0 macro.
  • 818. Esta orden provee informaci6n detallada a cerca de varias porciones del programa. Permite examinar funciones, variables, tipos y macros. Utilizar esta orden para ver con respecto a cada funci6n, que funciones llama y que variables, tipos y macros utiliza. Presenta una estructura en arbol, la cual permite ver que funciones llaman otras funciones. Visualiza un fichero que no este en linea. Si introducimos un *.*, entonces se visualizan todos los ficheros.
  • 819. Cuando se selecciona esta opcion, aparecen dos ventanas: una para el Brow- ser y otra para la ventana activa. EI menu de ayuda aparece cuando se pulsan las teclas AU + H. En el apare- cen las clases de ayuda, que a continuacion describimos. /H Modo 9rafico de MaS alta resoluci6n H/ if (! setvideOMode( MAXRESMODE» exit( 1 ); - /H Grilficos ~r.onteots Shlft"FI TopIc' ch~rtenv FI Help on Help Nex t· Chll rlenv -P9_defaultchart(£ent. _PG_BARCHART. _PG_PLAINBARS); strcpy(ent.Maintitle.title. "Producci6n de aceite de oliva"); -P9_chart(£ent. eleMentos. valor. PAISES); getch( ); _clearscreen(_GCLEARSCREEN); -P9_defaultchart(£ent. _PG_COLUMNCHART. PG PLAINBARS); strcpy(ent.Maintitle.title. "Producci6n de aceite de oliva"); -P9_chart(£ent. eleMentos. valor. PAISES); ~ ~ l)lew lnriex of hplp tOPICS r. 00017.0or, Para obtener informacion a cerca de una orden de un menu cualquie- ra, utilizar las teclas del cursor para elegirla y pulsar Fl 0 apuntarla con el raton y pulsar el boton derecho. Para obtener informacion a cerca de una palabra clave, un operador o una funcion, poner el cursor sobre dicho elemento y pulsar Fl 0 apun- tarla con el raton y pulsar el boton derecho.
  • 820. Indice alfabetico de todas las palabras reservadas utilizadas por el com pi- lad or C (palabras clave, funciones, tipos, etc.). Para utilizarlo: 2. Elegir la palabra clave deseada, moviendo el cursor allugar de la misma, utilizando las teclas de movimiento del cursor PgDn y PgUp. 1. Elegir la materia deseada moviendo el cursor al lugar donde se indica. Nos proporciona ayuda sobre cualquier palabra clave C. Esta palabra pue- de pertenecer al program a actual 0 al fichero de ayuda de Microsoft C. Para obtener esta ayuda:
  • 822. • Un ordenador IBM PC 0 compatible con un sistema operativo DOS version 3.0 0 superior, 0 OS/2 version 1.1 0 superior. • Un procesador 8088 minima 8 Mhz. Se recomienda un procesador 80286. • 384K de memoria extendida, si tenemos que depurar programas largos. • Una unidad de disco duro con un minima de 8 Mb disponibles y una unidad de disco flexible.
  • 823. 3. SETUP crea 10s ficheros NEW-VARS.BAT, NEW-CONF.SYS y TOOLS.PRE. E1 contenido del fichero NEW-CONF.SYS debe ser puesto en e1 fichero CONFIG.SYS. Las 6rdenes contenidas en el fichero NEW-VARS.BAT tienen que ejecutarse antes de arrancar Microsoft C; si 10 desea puede incluirlas en e1 fichero AUTOE- XEC.BAT. Renombrar TOOLS.PRE por TOOLS.INI.
  • 824. Si durante el proceso de instalaci6n se equivoca, interrumpa dicho pro- ceso pulsando las teclas Ctrl +C y ejecute de nuevo el programa SE- TUP.EXE. Este nunca borra ficheros de los discos del paquete Microsoft C. Para instalar Microsoft C inserte el disco SETUP en la unidad A, cambie a la unidad A (A: <Enter» y emita la orden: esta opci6n es uti! cuando una vez instalado Microsoft C necesitamos afiadir mas librerias combinadas. Esto evita el tener que realizar de nuevo la instalaci6n. esta opci6n suprime la informaci6n de ayuda enviada a la pantalla durante el proceso de instalaci6n. Una vez ejecutada la orden SETUP, se nos va preguntando las cues- tiones necesarias para realizar la instalaci6n. Presentamos a continuaci6n un ejemplo de una posible instalaci6n. IMPORTANTE: Si usted no ha leido la informaci6n referente al programa SETUP localizada en el manual Microsoft C Develoment System "Insta- lling and Using", por favor hagalo antes de continuar con el proceso de instalaci6n. Este documento, junto con el fichero README.DOC locali- zado en el disco SETUP, contiene informaci6n importante. Si usted no esta seguro de alguna respuesta, haga suya la respuesta por defecto. Si mas tarde considera que quiere realizar otras elecciones a las realizadas, ejecute SETUP otra vez.
  • 825. Usted es preguntado por la unidad de disco desde la cual va a efectuar la instalaci6n. Host Operating Mode: OS/2 Protect Mode [N]: N OS/2 Real Mode and DOS [Y]: Y El compilador C puede correr bajo OS/2 y bajo DOS. Elija el modo en el cual va a desarrollar sus programas. Se pueden especificar ambos modos. Target Operating Mode: OS/2 Protect Mode [N]: N OS/2 Real Mode and DOS [Y]: Y El compilador C puede correr bajo OS/2 y bajo DOS. Elija el modo bajo el cual va a ejecutar sus programas. Se pueden especificar ambos modos. i,Desea construir librerias combinadas? Para ganar velocidad el compila- dor C, para cada modelo, junta las librerias individuales en una sola libreria. Hay tres opciones para soportar las operaciones en punta flotante. El emu- lador utiliza el coprocesador matematico si esta presente; si no 10 esta, 10 emula mediante software. Esta es la opci6n par defecto. La libreria 80x87 utiliza el coprocesador matematico si esta presente; si no 10 esta, ocurre un error. La libreria matematica alternativa nunca utiliza coprocesador. Memory models: Small [Y]: Y Medium [N]: N Compact [N]: N Large [N]: Y Microsoft C soporta librerias para seis modelos diferentes de memoria. Usted puede elegir cualquiera de ellos 0 todos. Se recomienda seleccionar los mo- delos que utilice mas a menu do.
  • 826. Cuando SETUP finaliza la construcci6n de las librerias combinadas elegi- das, las librerias fuente a partir de las cuales se ha realizado la construc- ci6n, residen aun en el disco. Elias pueden ser borradas al finalizar la ins- talaci6n para liberar espacio en el disco. Include in combined libraries: GRAPHICS.LIB [N): Y PGCHART.LIB[N): Y l,Incluye en las librerias combinadas las librerias gnificas GRAPHICS.LIB y PGCHART.LIB? La libreria GRAPHICS.LIB se utiliza para graficos en general y la libreria PGCHART.LIB se utiliza para presentaciones gia- ficas (diagramas). Si usted esta conforme con las respuestas pulse 'N' para continuar. Si quiere modificar alguna de Ias respuestas pulse 'Y'. Si usted quiere salir pulse 'Q'. Si se va a utilizar un rat6n (Microsoft Mouse) responder 'Y' para instalar el fichero MOUSE.COM. Hay varios ficheros con documentaci6n acerca de esta versi6n como READ- ME.DOC. Nosotros Ie aconsejamos que Ios copie.
  • 827. Si usted tiene el sistema operativo de IBM(R) PC-DOS 3.20, puede que necesite parchear el sistema operativo utilizando estos ficheros, para mani-' pular errores en coma flotante. Si usted esta conforme con las respuestas, pulse 'N' para continuar. Si quiere modificar alguna de las respuestas pulse 'Y'. Si usted quiere salir pulse 'Q'. Directorio para los ficheros que pueden ejecutarse en ambos modos (real y protegido). En esta pregunta y siguientes, si el directorio especificado no existe, se nos preguntara si deseamos crearlo. Responder "Y". Directory for Real mode (DOS) executable files [C: C600 BIN]: C: C600 BIN
  • 828. Si usted esta conforme con las respuestas, pulse 'N' para continuar. Si quiere modificar alguna de las respuestas pulse 'Y'. Si usted quiere salir pulse 'Q'. A continuaci6n se nos van pidiendo los discos, para copiar los fiche- ros que componen el paquete Microsoft C en los directorios especificados.
  • 829. PARTE 8 Apendices • Ficheros .h de C • C6digos de Caracteres (ASCII) • Indice Alfabetico
  • 830. A continuacion se detallan 10s ficheros de cabecera (.h) correspondientes a C y las declaraciones que contienen.
  • 832. _psp _osmajor _osmode _doserrno struct WORDREGS struct BYTEREGS union REGS struct SREGS struct find_t struct DOSERROR struct dosdate_t struct dostime_t bdos _dos----,getdate _chain~ntr _dos----,getdiskfree _disable _dos----,getdrive _dos_allocmem _dos----,getfileattr _dos_close _dos----,getftime _dos_creat _dos~ettime _dos_creatnew _dos_getvect _dos_findfirst _dos_keep _dos_findnext _dos_open _dos_freemem _dos-fead _dos_setblock _dos_setdate _dos_setdrive _dos_setfileattr _dos_setftime _dos_settime _dos_setvect _dos_write dosexterr _enable _harderr jardresume _hardretn int86 int86x intdos intdosx segread
  • 833. Definicioncompatiblede Microsoftde los modos de lecturay escriturapara la fundon open. _arc _clearscreen _displaycursor _ellipse _floodfill _getbkcolor ~etcolor _getcurrentposition _getfillmask ~etimage ~etlinestyle ~etlogcoord ~etphyscoord ~etpixel ~ettextcolor ~ettextposition ~etvideoconfig -imagesize _lineto _moveto _outtext _pie _putimage _rectangle -femapallpalette -femappalette _selectpalette _setacti vepage _setbkcolor _setcliprgn _setcolor _setfillmask _setlinestyle _setlogorg _setpixel _settextcolor _settextposition _settxtwindow _setvideomode _setviewport _setvisualpage _wrap on
  • 834. access dup2 mktemp tell chmos eof open umask chsize felelngth read unlik close isatty rename write creat locking setmode dup lseek sopen alloca _fmalloc ~free ~heapchk ~heapset ~heapwalk _fheapwalk _msize realloc sbrk stackavail ~emmax _nmsize _ffree _fheapchk _fheapset ~malloc _expand free _freect halloc callox _fmsize hfree malloc _memavl
  • 835. abs bessels fabs acos cabs floor asin ceil fmod at an cos frexp atan2 cosh htpot atof exp labs ldexp sin log sinh loglO sqrt matherr tan modf tanh pow memccpy memicmp memchr memset abort execl execle exclp execlpe execv execve execvp execvpe exit _exit getpid spawnl spawnle memcmp movedata spawnlp spawnlpe spawnv spawnve spawnvp spawnvpe system
  • 838. Funciones prototipo: bsearch fgetpos fscanf setvbuf calloc fgets fseek tempnam fsetpos puts tmpfile fclose flushall ftell putw tmpnam fcloseall fopen fwrite qsort ungetc fdopen fprintf remove vfprintf fputc rename vprintf fputchar gets rewind vsprintf fflush fputs getw rmtemp fgetc fread perror scanf fgetchar freopen printf setbuf abort ecvt Idiv perror srand avs exit -lrot! putenv strtos atexit _exit -lrotr qsort strtol atof fcvt Itoa rand strtoul atoi free ~akepath ralloc swab atol gcvt f malloc .-rot! system vsearch tetenv max .-rotr tolower calloc itoa min _searchenv toupper div labs onexit _splitpath ultoa _doserrno _osminor sYS--llerr _osmajor sys_errlist _fmode _psp errno _osversion environ _osmode
  • 842. CODIGOS DE CARACTERES ( ASCII) Valor Valor Caracter Caracter decimal hexadecimal de control 0 00 NUL Nulo 1 01 SOH Q 2 02 STX •3 03 ETX 'I 4 04 EOT •5 05 ENQ + 6 06 ACK + 7 07 BEL Zumbido, Pitido, "bip" 8 08 BS D 9 09 HT Tabulaci6n 10 OA LF Avance de linea 11 OB VT Cursor a posici6n inicial 12 OC FF Avance de pagina 13 OD CR Retorno de carro, introducir 14 OE SO ~ 15 OF SI i;1- 16 10 DLE • 17 11 DCl '4 18 12 DC2 I 19 13 DC3 !! 20 14 DC4 1r 21 15 NAK § 22 16 SYN - 23 17 ETB -L 24 18 CAN I 25 19 EM I 26 lA SUB -27 1B ESC - 28 IC FS Cursor a derecha 29 ID OS Cursor a izquierda 30 IE RS Cursor arriba 31 IF US Cursor abajo
  • 843. Valor Valor Caracter decimal hexadecimal 32 20 Espacio 33 21 ! 34 22 " 35 23 # 36 24 $ 37 25 0,70 38 26 & 39 27 , 40 28 ( 41 29 ) 42 2A •43 2B + 44 2C , 45 2D - 46 2E 47 2F / 48 30 0 49 31 1 50 32 2 51 33 3 52 34 4 53 35 5 54 36 6 55 37 7 56 38 8 57 39 9 58 3A : 59 3B ; 60 3C < 61 3D = 62 3E > 63 3F ? 64 40 @ 65 41 A 66 42 B 67 43 C 68 44 D 69 45 E 70 46 F 71 47 G 72 48 H 73 49 I 74 4A J 75 4B K 76 4C L 77 4D M 78 4E N 79 4F 0
  • 844. Valor Valor Caracter decimal hexadecimal 80 50 P 81 51 Q 82 52 R 83 53 S 84 54 T 85 55 U 86 56 V 87 57 W 88 58 X 89 59 Y 90 5A Z 91 5B [ 92 5C 93 5D ] 94 5E A 95 5F - 96 60 . 97 61 a 98 62 b 99 63 c 100 64 d 101 65 e 102 66 f 103 67 g 104 68 h 105 69 i 106 6A j 107 6B k 108 6C 1 109 6D m 110 6E n III 6F 0 112 70 p 113 71 q 114 72 r 115 73 s 116 74 t 117 75 u 118 76 v 119 77 w 120 78 x 121 79 y 122 7A z 123 7B ( 124 7C I I 125 7D I 126 7E - 127 7F Cl
  • 845. Valor Valor Caracter decimal hexadecimal 128 80 c; 129 81 ii 130 82 e 131 83 a 132 84 a 133 85 a 134 86 a 135 87 t; 136 88 e 137 89 e 138 8A e 139 8B i' 140 8C 1 141 8D i 142 8E A 143 8F A 144 90 E 145 91 ae 146 92 AE 147 93 0 148 94 6 149 95 0 150 96 u 151 97 U 152 98 Y 153 99 b 154 9A 0 155 9B t; 156 9C £ 157 9D r 158 9E Pt 159 9F f 160 AO a 161 Al i 162 A2 6 163 A3 U 164 A4 il. 165 A5 N 166 A6 a 167 A7 0 168 A8 l, 169 A9 .--170 AA ---, 171 AB Yz 172 AC Y4 173 AD i 174 AE « 175 AF »
  • 846. Valor Valor Caracter deCimal hexadecimal 176 BO 177 Bl 178 B2 ...... ...... 179 B3 I 180 B4 ., 181 B5 ~ 182 B6 -;1 183 B7 ""lI 184 B8 ., 185 B9 ~I 186 BA II 187 BB =;) 188 BC =!J 189 BD ~ 190 BE •• 191 BF ., 192 CO L 193 Cl ~ 194 C2 .,.. 195 C3 I- 196 C4 - 197 C5 + 198 C6 F 199 C7 It- 200 C8 t= 201 C9 IF 202 CA ~ 203 CB 'iF 204 CC 1:= 205 CD = 206 CE "'-,... 207 CF ••208 DO -"'- 209 D1 "I' 210 D2 .".. 211 D3 lL 212 D4 b. 213 D5 F 214 D6 rr 215 D7 + 216 D8 + 217 D9 .J 218 DA r 219 DB 220 DC •221 DD • 222 DE I 223 DF I 224 EO -ex
  • 847. Valor Valor Caracter decimal hexadecimal 225 El {3 226 E2 r 227 E3 'K 228 E4 E 229 E5 (J 230 E6 p. 231 E7 T 232 E8 41 233 E9 (J 234 EA 0 235 EB [) 236 EC 00 237 ED 0 238 EE ~ 239 EF n 240 FO = 241 Fl ± 242 F2 ~ 243 F3 s 244 F4 J 245 F5 r 246 F6 247 F7 "" 248 F8 0 249 F9 250 FA 251 FB r252 FC n 253 FD 2 254 FE •255 FF (espacioen blanco'FF')
  • 848. 3 15 16-25 30-38 44-50 59-68 71 72 73 75 77 79 80 81 82 83 84-93 94-103 104-113 114 115 116 117 118 119 120-131 132 133 134 135 136 137 138 139 140 NUL (null character) Shift Tab (- < + + ) Alt-Q/W/E/R/T/Y/UlI/O/P Alt-A/S/D/F/G/H/II J/K/L Alt-Z/X/C/V/B/N/M Keys FI-FIO (disabled as softkeys) Home Up arrow PgUp Left arrow Right arrow End Down arrow PgDn Ins Del F11-F20 (Shift-Fl to Shift-FIO) F21-F30 (Ctrl-Fl through FIO) F31-F4O (Alt-Fl through FIO) Ctrl-PrtSc Ctrl-Left arrow Ctrl-Right arrow Ctrl-End Ctrl-PgDn Ctrl-Home Alt-l/2/31 4/516/7 1819/01-1 = Ctrl-PgUp F11 F12 Shift-F11 Shift-Fl2 Ctrl-F11 Ctrl-Fl2 Alt-F11 Alt-F12
  • 849. 876 ENCICLOPEDIA DEL LENGUAJE C CODIGOS DEL TECLADO Tecla Codigo en Hex Tecla Codigo en Hex Esc OJ Left/Right orrow OF !l 02 Q 10 @2 03 W 11 #3 04 E 12 $4 05 R 13 %5 06 T 14 A6 07 Y 15 &7 08 U 16 ·S 09 I 17 (9 OA 0 18 )0 OB P 19 - OC {f lA += OD V 18 Backspace OE Enter lC Ctrl lD : 2B A IE Z 2C S IF X 2D D 20 C 2E F 21 V 2F G 22 B 30 H 23 N 31 J 24 M 32 K 25 <, 33 L 26 >. 34 ., 27 ?/ 35 28 RightShift 36 29 PrtScr· 37 LeftShift 2A Aft 38 Spacebar 39 7Home 47 Caps Lock 3A SUp arrow 48 Fl 3B 9PgUp 49 F2 3C 4A F3 3D 4Left arrow 4B F4 3E 5 4C F5 3F 6Right arrow 4D F6 40 + 4E F7 41 lEnd 4F FS 42 2Down arrow 50 F9 43 3PgDn 51 FIO 44 OIns 52 Fl1 D9 Del 53 Fl2 DA Num Lock 45 Scroll Lock 46
  • 850. INDICE ALFABETICO #define 91, 392 #error 400 #if 396 #ifdef e #ifndef 399 #include 91, 395 #line 399 #pragma 400 #undef 395 _arc 758 _asm 620 Definici6n de macros en lenguaje ensamblador 629 Liamando a funciones C 633 Manipulaci6n de interrupciones 635 Reemplazar una funci6n C 634 Saito a una etiqueta 640 Trabajando con estructuras 637 Trabajando con punteros 635 Utilizando elementos de C en un bloque Utilizando y salvando registros 630 _asm 626 _based 528 _bcalloc 541 _bfree 541 _bfreeseg 536 _bheapseg 535 _bios_disk 658 _bios_equiplist 660 _bios_keybrd 660 _bios~emsize 661 _bios_printer 662 _bios_serialcom 664 _bios_timeofday 666 _bmalloc 541 _brealloc 541 _chaia-jntr 687 _clearscreen 754 _disable 687 _displaycursor 742 _dos_allocmem 687 _dos_close 687 _dos_creat 688 _dos_creatnew 688 _dos_findfirst 688 _dos_findnext 688 _dos.-freemem 688 _dos~etdate 688 _dos~etdiskfree 689 _dos~etdrive 689 _dos~etfileattr 689 _dos~etftime 689 _dos~ettime 689 _dos~etvect 689 _dos_keep 689
  • 851. _dos_open 690 _dos-fead 690 _dos_setblock 690 _dos_setdate 690 _dos_setdrive 690 _dos_setfileattr 691 _dos_setftime 691 _dos_settime 691 _dos-setvect 691 _dos_write 691 _ellipse 757 _emit 625 _enable 691 _expand 541 _far 527 _fcalloc 541 _ffree 541 _flood fill 753 _fmalloc 541 _fmemccpy 547 _fmemchr 547 -fmemcmp 547 -fmemcpy 547 _fmemicmp 547 -fmemmove 547 _fmemset 547 _fpreset 705 _frealloc 541 _fsopen 353 ~etbkcolor 750 ~etcolor 750 ~etcurrentposition 760 ~etfillmask 752 ~etfontinfo 799 ~etgtextextent 799 ~etimage 764 ~etlinestyle 751 ~etphyscoord 746 ~etpixel 760 ~ettextcolor 761 ~ettextposition 762 ~etvideoconfig 743 ~etviewcoord 746 ~etwindowcoord 778 -harderr 692 -hardresume 692 -hardretn 692 -huge 528 -.imagesize 764 -.-lineto 755 -fUoveto 755 -Jlcalloc 540 -Jlear 527 _nfree 541 -Jlmalloc 540 -Jlrealloc 541 _outgtext 799 _outtext 762 _p~chart 788 _p~chartms 791 _p~chartpie 789 _p~chartscatter 793 _p~chartscatterms 793 _p~defaultchart 787 _pg-.initchart 787 _pie 758 _putimage 765 -fectangle 756 -fegisterfonts 797 -femapallpalette y -femappalette 748 _selectpalette 749 -setactivepage 744 _setbkcolor 749 _setcliprgn 747 _setcolor 750 _setfillmask 751 _setfont 797 _setgtextvector 800 _setlinestyle 751 _setpixel 760 -settextcolor 761 _settextposition 761 -settextwindow 762 _setvideomode 743 _setvideomoderows 743 _setvieworg 745 _setviewport 747 -setvisualpage 744 _setwindow 775 _unregisterfonts 800 _wrapon 762 abort 701 Abrir un fichero 317, 319, 356, 357 abs 296 Accesibilidadde variables. Ambito 101 Acceso aleatorio 319, 348, 366 Acceso secuencial 319 access 679 acos 293 Algoritmos hash 497 Animaci6n 764 Animaci6n de un objeto 766 Arboles 449 Arboles binarios 450
  • 852. Borrado en arboles 458 de busqueda 453 perfectamente equilibrados 460 Recorrido de arboles binarios 451 Argumentos en la linea de 6rdenes 274 Arrays 163 de cadenas de caracteres 182 de estructuras 200 de punteros 233 Declaraci6n ge un array 164 dimimicos 242 hash 498 multidimensionales 165 unidimensionales 164 Asignaci6n dinamica de memoria 239, 414 asin 293 asserLh 857 atan 294 atan2294 atexit 702 atof 190 atoi 190 atol 190 bdos 653 BIND 617 bios.h 857 break 138 bsearch 305 Buffer asociado con stdin 176 Busqueda binaria 486 Busqueda de datos 485 Busqueda secuencial 485 Cadenas de caracteres 173 calloc 243 Camino 669 Campos de bits 207 Caracter fin de linea y caracter fin de fichero 123 Caracteres de C 52 Caracteres especiales y signos de puntuaci6n 53 cast 85 ceil 296 Cerrar un fichero 318, 323, 356, 363 cgets 376 char 54 chdir 674 chmod 680 chsize 682 CL 550 Clases de almacenamiento 103 Clasificaci6n de datos 473 clearerr 124, 324 clock 300 close 363 Code View 596 Calls 616 Com pilar y enlazar un programa C para depurar 597 con rat6n (n:lOuse)603 Edit 605 File 604 Invocando a Code View 597 Menus de Code View 601 Opciones de Code View 598 Options 614 Run 611 Search 609 Seleccionando texto 604 View 606 Watch 612 c6digos de salida 686 Colas 432 Colores en modo grafico utilizando CGA 736 Colores en modo grafico utilizando VGA, MCGA y EGA 739 Colores en modo texto 734 Comentarios 70 Comenzar' un nuevo proceso 712 compact 519 Compilaci6n 47 Compilaci6n condicional 396 Compilar y ejecutar el prograrna 44 conio.h 858 const 72 Constante de un solo caracter 67 Constantes 64 Constantes de caracteres 67 Constantes enteras 65 Constantes reales 66 continue 149 Control del cursor 378 Conversi6n de tipos 82 Conversi6n explicita del tipo de una expresi6n 85 Convertir coordenadas fisicas a 16gicas y viceversa 741 Coordenadas fisicas 740 16gicas 740 reales en una ventana 775 cos 294 cosh 295
  • 853. cprintf 377 cputs 377 Creaci6n de una enumeraci6n 58 Creaci6n de una mascara 753 Crear un fichero ejecutable 550 creat 360 cscanf 377 ctime 300 ctype.h 858 Cuerpo de la funci6n 255 CVPACK 616 Declaracion de constantes 72 Declaraci6n de funciones a nivel interno y a nivel externo 108 Declaracion de funciones far 0 near 530 Declaracion de una funci6n 95, 257 Declaraci6n de variables near, far, huge 0 based 529 Declaraciones complejas 248 Declaraciones y definiciones 92 defined 397 Definici6n de una funci6n 96, 252 Depuracion 47; 596 Depurar un programa 45 Detecci6n de errores 318, 324 direct.h 858 Directorios y carninos 669 div 302 do 143 dos.h 859 dosexterr 691 double 60 dup 369 dup2369 Edici6n 45 Edici6n de un programa 42 Ejecuci6n de procesos 702 Ejecuci6n de un proceso 699, 702 ensamblador 619, 641 Entrada y salida estandar 110 Entrada/salida caracter a caracter 326 con formato 336 de cadenas de caracteres 332 palabra a palabra 330 utilizando registros 0 bloques 338 enum 57 eof 361 errno.h 860 Espacios en blanco 52 Espedficaci6n de un path 673 Estructura de un programa C 89 Estructura de un programa gnl.fico 726 Estructura de un programa para presentaciones graficas 785 Estructura para almacenar la configuracion de video 731 Estructuras 197, 413 Creaci6n de una estructura 197 Operaciones con estructuras 200 execxxx 712 EXEHDR 617 exit 702 exp 295 EXP 618 Expresiones 94 condicionales 79 de Boole 76 numericas 73 de ficheros 553 Extensiones fabs 2% fclose 323 fcloseal1 323 fcntl.h 860 fcvt 191 fdopen 321 feof 325 ferror 324 fflush 124, 344 fgetc 328 fgetpos 352 fgets 333 ficheros de cabecera (.h) 91, 403 Ficheros temporales 345 fileno 362 float 59 float.h 860 floor 2% fopen 319 for 145 for bucles anidados 146 FP_OFF 653 FP-1)EG 654 fprintf 336 fputc 326 fputs 332
  • 854. fread 338 free 245 freopen 322 fscanf 336 fseek 348 fsetpos 352 ftell 349 Funciones 95 con un numero de argumentos variable 276 gnificas 742 intrinsecas 401, 407 matemliticas 292 para asignacion dimimica de memoria 240 para clasificacion y conversion de caracteres 193 para control de directorios 674 para control de procesos 701 para conversion de datos 190 para entrada/salida 363 para la consola 373 para llamar al DOS 650 para los puertos de E/S 384 para manipulacion de ficheros 679 para manipular cadenas de caracteres 183 para obtener 0 poner atributos 749 para presentaciones gnificas 787 para representar distintos tipos de letras 797 para un sistema de coordenadas cartesianas 779 predefinidas en C 292 prototipo 257 recursivas 279 referentes al uso de paletas 748 re1ativas a configuracion 742 relativas a coordenadas 745 fwrite 338 getch 125, 373 getchar 122 getche 125, 374 getcwd 676 getenv 720 gets 175 getw 330 goto y etiquetas 150 graph.h 860 halloc 246 hash abierto 499, 503 hash con overflow 501 hash. Eliminacion de elementos 502 HELP MAKE 617 hfree 246 Historia del lenguaje C 39 huge 523 Identificadores 68 if 129 if anidamiento de sentencias 131 if estructura 134 IUNK 568 Iniciacion de un proceso 698 Inicializacion de cadenas 232 inp 385 inpw 386 Instalacion 848 int 56 int86650 int86x 651 intdos 651 intdosx 652 Interconexion de entradas y salidas estandar 672 io.h 861 isalnum 193 isalpha 193 isascii 193 isatty 685 iscntrl 194 isdigit 194 isgraph 194 islower 194 isprint 194 ispunct 194 isspace 195 isupper 195 isxdigit 195 itoa 191 labs 296 large 520 Letras, digitos y caracter de subrayado 52 Leyendo y escribiendo datos 318 lfind 306
  • 855. LIB 572 LIB con respuestas automaticas 576 LIB en modo pregunta/respuesta 575 limits.h 861 LINK 560 LINK con respuestas automaticas 566 LINK en modo pregunta/respuesta 565 Listas circulares 436 Listas doblemente enlazadas 444 Listas lineales 415 Operaciones basicas 418 Llamada a una funcion 97, 256 Llamando a un pr0cedimiento en ensamblador desde C 644 localtime 301 log 295 log10 296 long 56 long double 61 longjmp 704 lsearch 306 lseek 366 ltoa 192 macros 392 macros 0 funciones 408 malloc 240 malloc.h 861 manipulacion de bloques de memoria 542 Manipulacion de ficheros 316, 335 math.h 862 matherr 297 medium 518 memccpy 542 memchr 543 memcmp 543 memcpy 544 memicmp 543 memmove 544 memoria intermedia asociada con un fichero 341 memory.h 862 memset 544 Menus de PWB 806 Metodo de insercion 477 Metodo de la burbuja 473 Metodo Quicksort 480 mkdir 675 Modalidades de video disponibles 728 Modelos de meploria estandar 515 Modelos de memoria mixtos 526 NMAKE 579 Caracteres que pueoen modificar macros 585 Componentes de una descripcion de fichero 591 con respuestas automaticas 592 Directrices 588 Ficheros en linea 590 Inicializacion automatica. TOOLS.INI 593 Macros 583 Macros especiales 584 makefile 579 Opciones de NMAKE 582 Prioridades 589 Pseudoobjetivos 592 Reglas de inferencia 586 Simbolos especiales 591 Sustituciones en macros 584 Nombres de ficheros y extensiones 49 Nombres de tipos 63 Numeros pseudoaleatorios 158 Ca1culo de areas y volumenes 161 onexit 703 Opciones de CL 554 open 357 Operaciones con directorios 673 Operador # 394 # # 394 coma 79 de direccion-de (&) 80 de indireccion (.) 80 sizeof (tamafto de) 80 Operadores 73 aritmeticos 74 de asignacion 77 de relacion 75 logicos 74 logicos para manejo de bits 76 unitarios 76 Ordenacion de ficheros en disco 488 Acceso aleatorio 494 Acceso secuencial 488 outp 385 outpw 386 Overlays 567 Palabras clave 69 Parametros de inicializacion del puerto 664
  • 856. Parametros por valor 0 por referencia 267 Pasando argumentos a funciones 98, 268 perror 326 Pilas 427 pow 297 Preparando un programa simple 45 preprocesador 391, 405 Presentacion de la sintaxis de C 51 printf 110 Prioridad y orden de evaluacion 81 process.h 862 Programa C formado por multiples ficheros 99 Programas residentes 692 Prompt 672 Punteros 22, 219 a cadenas de caracteres 227, 236 a estructuras 247 a funciones 288 a objetos de tipo no especificado (void) 225 a punteros 233 basad os en su propio segmento 539 basad os en un segmento 514 basados en un segmento constante 532 basados en un segmento variable 533 basados en void 538 basados sobre un puntero 536 Comparacion de punteros 223 Creacion de punteros 219 far 511 huge 514 near 511 nulos 524 Operacion de asignacion 222 Operaciones aritmeticas 222 Operaciones con punteros 222 Operadores 220 y arrays 226 y segmentos de 64K 510 putch 374 putchar 122 putenv 720 puts 175 putw 330 PWB 805 Browse 842 Caracteristicas del editor del PWB 814 con raton (mouse) 809 Copiar texto de otros ficheros 829 Edit 818 File 811 menu principal 808 Help 844 Make 831 Moviendo y copiando texto 821 Operaciones con el editor 816 Options 836 PWB Run 835 Search 824 Seleccionando texto 815 Ventanas de dialogo 810 View 822 QH 617 qsort 303 raise 710 rand 299 read 363 Realizacion de un programa en C 42 realloc 244 Recursividad 465 Redireccion de la entrada 671 Redireccion de la salida 671 remove 683 rename 683 Restaurar la modalidad de video original 730 return 97, 255 rewind 349 RM 618 rmdir 675 rmtmp 345 Rutinas en lenguaje ensamblador en linea con sentencias C 620 Salvar el programa 44 scanf 116 search.h 863 Secuencias de escape 53 segread 653 Seleccionar la modalidad de video 729 Sentencia compuesta 0 bloque 94 Sentencia de asignacion 109 Sentencias 94 Servicios del BIOS 658 setbuf 341 setjmp 704 setjmp.h 863 setmode 683 SETUP 849 setvbuf 341
  • 857. share.h 863 short 55 signal 707 signaI.h 863 sin 294 sinh 295 Sintaxis de las sentencias y funciones de C 108 small 517 sopen 369 Soporte MS-DOS para llamadas al sistema 687 para asignaci6n de memoria 540 para cadenas de caracteres 542 para manipulaci6n de bloques de memoria 546 spawnxxx 716 sprintf 730 sqrt 297 srand 299 stack 281 stat 684 stdarg.h 863 stddef.h 864 stdio.h 864 stdlib.h 865 strcat 183 strchr 184 strcmp 184 strcpy 184 strcspn 184 strdup 188 strerror 189 string.h 866 strlen 185 strlwr 189 strncat 186 strncmp 186 strncpy 186 strnset 189 strrchr 186 strset 189 strspn 187 strstr 187 strtok 187 strupr 189 switch 135 sys locking.h 866 sys stat.h 866 sys timeb.h 866 sys types.h 867 sys utime.h 867 system 126, 677 Tamafio de una variable tipo puntero 510 tan 295 tanh 295 tell 367 tempnam 347 Terminaci6n de procesos 700, 701 time 300 time.h 867 tiny 516 Tipos de datos 53 Tipos de letras (fonts) 796 Tipos derivados 61 Tipos estandar 86 Tipos fundamentales 54 tmpfile 345 tmpnam 346 toascii 195 tolower 195 toupper 196 typedef 63 umask 370 UNDEL 618 ungetch 375 Uniones 201 unlink 682 Utilizaci6n de dispositivos estandar 334 Utilizaci6n de punteros basad os en un segmento 530 utime 685 varargs.h 867 Variables 70 dedaradas a nivel externo 104 dedaradas a nivel interne 106 globales y locales 101 Visualizar imagenes 754 Visualizar texto 761 void 61 volatile 73 while 140 write 363
  • 858. Del mismo autor • Curso de programaci6n con PASCAL ISBN: 84-86381-36-3 224 pags. • Curso de programaci6n GW BASIC/BASICA ISBN: 84-86381-87-8 320 pags. • Manual para TURBO BASIC Guia del Programador • Manual para Quick C 2 Guia del programador • Manual para Quick BASIC 4.5 Guia del programador • Curso de programaci6n con C Microsoft C ISBN: 84-86381-43-6 444 pags. ISBN: 84-86381-65-7 540 pags. ISBN: 84-86381-74-6 496 pags. ISBN: 84-7897-052-5 512 pags. • Curso de programaci6n Microsoft COBOL ISBN: 84-7897-001-0 480 pags. • Curso de programaci6n C++ Programaci6n Orientada a Objetos ISBN: 84-7897-034-7 784 pags.
  • 859. EnciclopediadellenguajeC. C es uno de los lenguajes de programaci6n mas populares en la actualidad. permite realizar una programaci6n estructurada sin lfmite a la creatividad del programador V ademas los compiladores C tienen la ventaja de produci:r programas reducidos y muv rapidos en ejecuci6n. Por ello, muchos paquetes de &oftware estan escritos en C. . Ellibro ha sido escrito p~ns~ndo par una parte en las personas que no tenie'ndo cono~imientos de programaci6n C, desean aprenderlos; y por otra, para los programadorts expertos, los cuales encontraran una exposici6n completa dellenguaje C con todas las caracterfsticas que posee, V 10 que con el se puede realizar. En resumen, incluve temas referentes a: • Programaci6n C en base al disefio Top Down. • Sentencias! funciones V punteros. :: . • Estructuras, uniones, enumeraciones, cadenas V arrays. • Ficheros. Acceso secuencial V aleatorio. • EI preprocesador. • Estrw:turas dinamicas.(listas V arboles). • Algoritmos recursivos, de ordenaci6n V de busqueda. • Manejo de Ia memoria. • Compilador V eniazador .. ". Librerfas y utilidades. • Rutina~ en Ienguaje ensamblador. • Servicios del DOS y del BIOS. • Graficos. . • Entorno integ,rado de desarrollo. Todo esto y mas se exp~me de forma clara y sencilla, con alrededor de 175 PROBLEMAS RESUELTOS que Ie avudaran V serviran de base para &USaplicaciones. 9 780201 625066 ISBN 0-201-62506-7 ~J;.~ADDlsoN-wEsLE~ ..iBERoAMERlcANA Billinghurst 897 PB-A, Buenos Aires 1174, Argentina Ave. Brigadeiro Luis Antonio 2344, Conjunto 114, Sao Paulo 01402, Sao PalJlo, Sr.lsil Casilla 70060, Santiago 7, Chile Apartado Aereo 241-943, Santa Fe de Bogota, Colombia Espalter 3 baja, Madrid 28014, Espana 7 Jacob Way, Reading, Massachusetts 01867, E.U.A. Apartado Postal 22-012, Mexico D.F. 14000, Mexico Apartado Postal 29853, Rio Piedras, Puerto Rico 00929 Apartado Postal 51454, Caracas 1050·A, Venezuela