Vous êtes sur la page 1sur 859

Enciclopedia del Lenguaje

""."" ADDISON-WESLEY IBEROAMERICANA


Enciclopedia del Lenguaje
c
Enciclopedia del Lenguaje
c
Feo. J avier Ceballos Sierra
Profesor titular de la
Escuela Universitaria Politecnica
Universidad de Alcala de Henares (Madrid)
TAT ADDISON-WESLEY IBEROAMERICANA
Sehapuesto el maximo empefio enofrecer allector una informacion
completa yprecisa. Sinembargo RA-MAEditorial yAddison-Wesley
Iberoamericana, S.A.no asumen ninguna responsabilidad derivada de
suusa, ni tampocopor cualquier violacion depatentes ni otros
derechos deterceras partes quepudieran ocurrir.
1993por Addison WesleyIberoamericana, S.A.
Wilmington, Delaware, E.U.A.
Ninguna parte deestelibro puede ser reproducida, grabada ensistema
dealmacenamiento 0transmitida enforma alguna ni por cualquier
procedimiento, yaseaelectronico, mecanico, reprografico, magnetico 0
cualquier otro, sinautorizacion previa ypor escrito deRA-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, digitosycarcicterdesubrayado. ........... ... 52
Espacios en blanco. ............... ................ 52
Caracteres especialesy 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 0bloque. . . . . . . . . . . . . . . . . . . . . . 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 globales y locales. . . . . . . . . . . . . . . . . . . . . . . . . 101
Clases de almacenamiento. . . . . . . . . . . . . . . . . . . . . . . . . . 103
Variables declaradas a nivel externo. . . . . . . . . . . . . . . . . 104
Variables declaradas a nivel interno. . . . . . . . . . . . . . . . . . 106
Declaraci6n de funciones anivel interno y anivel 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 ycaracter fin de fichero. . . . . .. . . . . 123
Funciones getch y getche. . . . . . . . . . . .. . . . . . . . . . .. . . . . . 125
Funci6n system..................................... 126
Ejercicios . .. . . . . . . . . . .. . .. . . . . . . . . . .. . . . . . . . . .. . . . . 126
CAPITUW 4. Sentencias de Control.................... 129
Sentencia if. . .. . . . .. . . . . . . . . . . . .. . . . . . . . . . .. . . . . . . .. 129
Anidamiento de sentencias if.. . . . . .. . . . . . . . . . .. . . . . . . 131
Estructura if. . . . . . . . . . .. . . . . . . . . . .. . . . . . . . . .. . . . . . . . 134
Sentencia switch. . . . . . .. . . . . . . . . .. .. . . . . . . . . . .. . . . . . . 135
Sentencia break..................................... 138
Sentencia while..................................... 140
Sentencia do. . . . . . . . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . 143
Sentencia 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 decaracteres. . .. . . . .. 183
Funciones para conversi6n de datos. . . . .. . . . . . . . .. . . . .. 190
Funciones para clasificaci6n yconversi6n 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 seapunta. . . . . . 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 yarrays. . . . . . .. . . . . . . . . . . .. . . . . . . . . . . .. . . . . 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 0por 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 yacceso 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 0bloques. . . . . . .. . . . 338
fwrite . . . .. . . . . . . . .. . .. . . . . . .. . . . .. . . . . . . . .. . . . . . 338
fread . . . . . . . . . . .. . . .. . . .. . . . . . . .. . . . .. . . . . ... . .. . 338
Control dela 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 0funciones. . . . . . . . . . . . . . . . . . . . . . 408
CAPITUW 12. EstructurasDimimicasdeDatos.......... 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 deuna listacuyo 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, 0based. . . . . . . . 529
Declaraci6n de funciones far 0near. . . . . . . . . . . . . . . . . . . 530
Utilizaci6n de punteros basados 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
Compilar 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 desde 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 deProcesos. . . . . . . . . . . . . . . . . . . . . 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 0poner 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. PresentacionesGnificas. . . . . . . . . . . . . . . . . . 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. Utilizaci6ndel 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 0mas 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 e1minimo 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 1a 12dado. . . . . . . .. 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 yfree. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 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 0numericamente. . . . . . . . . . . . . .. 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 1aconsola. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 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 deecuaciones 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 deAckerman 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
Variablesy 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 dememoria , , . . .. 544
Un ejemplo de trabajo con librerias. . . . . . . . . . . . . . . . . . . . . . .. 577
Fichero de descripciones. NMAKE......................... 580
Un ejemplo detrabajo 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 devideo. . . . . . . . . . . . . . . . . . . . . . . . . . .. 745
Portada para Microsoft C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 756
Colorear figuras......................................... 759
Ventanas de texto..................... . . . . . . . . . . . . . . . . . .. 763
Funciones para animaci6n de figuras. . ..... 767
Animaci6n - pe10tarodando. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 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 deletras. . . . . . . . . . . . . . . . . . . . . . . . . . .. 800
Esteesun libro quepretende cubrir dos objetivos: ser un manual para
aprender C y ser una guia para el usuario de C.
La forma en la que seha estructurado ellibro ha sido precisamente,
pensando en ese primer objetivo. El libro seha dividido en veinticuatro
capitulos que van presentando ellenguaje poco a poco, empezando por
10mas sencillo, presentando cada tema a su tiempo, hasta llegar al final
donde sehabra visto todo 10referente alaprogramacion enC yutilidades,
sin apenas encontrar dificultades.
El segundo objetivo queda conseguido al incluir enestelibro todo 10
queun usuario qui eresaber respecto aC, esto es, explicacion y desarrollo
detodas las sentencias, estructuras, punteros, funciones, ficheros y direc-
trices para compilador. Secompleta el estudio de C con un capitulo refe-
rente a estructuras dinamicas y otro de algoritmos de uso comun. Final-
mente seestudian tecnicas avanzadas que abordan la materia referente a
manejo delamemoria, compilacion yenlace, librerias, rutinas enlenguaje
ensamblador, utilidades como el depurador deC, servicios del DOS y del
BIOS, grcificosyuna explicacion para el manejo del entorno deprograma-
cion del paquete de Microsoft C.
Microsoft eversion 6, producto desarrollado por Microsoft, esuncom-
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 decorrer bajo MSDOS y OS/2, inclu-
ye tambien soporte para Microsoft Windows.
Este libro posee varias caracteristicas dignas de resaltar. Es breveen
teoria yabundante enejemplos, 10queIehara aun mas facil: el aprendiza-
je para aquellas personas que quieren iniciarse en el tema, 0la busqueda
ycompresi6n deun tema puntual para aquellas otras personas entendidas
en programaci6n C. La metodologia utilizada en el desarrollo delos pro-
gramas esladescomposici6n arriba-abajo (top down). Es un libro facil de
entender.
La materia total que compone la Enciclopedia dellenguaje C, seha
dividido en los siguientes capitulos y apendices:
Todoestosehadocumentado conalrededor de 175PROBLEMAS RE-
SUELTOS, utilizando laprogramaci6n estructurada, muchos deellos vali-
dos como parte integrante en el desarrollo de aplicaciones.
Herecibido ayuda dealgunas personas durante lapreparaci6n deeste
libro, ypor elloestoy francamente agradecido. En especial quiero expresar
mi agradecimiento alafirma Microsoft, por laayuda 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 esun lenguaje deprogramaci6n deprop6sito general. Susprincipales
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 enlos Laboratorios Bell deAT&Tyha sido estre-
chamente asociado con el sistema operativo UNIX, ya que su desarrollo
serealiz6 enestesistema y debido aque 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 estainspirado enellenguaje Bescrito por KenThomp-
son en 1970con intenci6n derecodificar el UNIX, queenlafasedearran-
que estaba escrito en ensamblador, envistas asutransportabilidad aotras
maquinas. Beraun lenguaje evolucionado eindependiente delamaquina,
inspirado en ellenguaje BCPL concebido por Martin Richard en 1967.
En 1972,Dennis Ritchie, toma el relevoymodifica ellenguaje B, crean-
do ellenguaje C y reescribiendo el UNIX en dicho lenguaje. La novedad
que proporcion6 ellenguaje C sobre el Bfueel disefio detipos yestructu-
ras de datos.
Lostipos basicos dedatos eranchar (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 delongitud ~longitud deun int), unsigned (enteros sinsig-
no) y enumeraciones. Los tipos estructurados basicos deC son las estruc-
turas, las uniones y los arrays. Estos permiten la definici6n y declaraci6n
de tipos derivados de mayor complejidad.
Las instrucciones decontrol deflujo deC son lashabituales delapro-
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 sepasan por valor, esto es copiando su valor, 10cual hace que no
semodifiquen los valores delos argumentos enlaHamada. Cuando sede-
seamodificar los argumentos enlaHamada, estos sepasan por referencia,
es decir, sepasan 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 seriedeoperaciones quepueden hacerse conellenguaje
C, que realmente no estan incluidas en el compilador propiamente dicho,
sino quelas realizaun preprocesador justa antes decada compilaci6n. Las
dos mas importantes son #define (directriz desustituci6n simb6lica 0de
definici6n) e #include (directriz de inclusi6n en e1fichero fuente).
Finalmente, C, que ha sido pensado para ser altamente transportable
yparaprogramar 10improgramable, igual queotros lenguajes tiene sus in-
convenientes. Carece de instrucciones de entrada/salida, deinstrucciones
para manejo decadenas decaracteres, con 10que estetrabajo queda para
lalibreria derutinas, con laconsiguiente perdida detransportabilidad. La
excesivalibertad enlaescritura delos programas puede llevar aerrores en
la programaci6n que, por ser correctos sintacticamente no se detectan a
simplevista. Por otra parte las precedencias delos operadores convierten
aveceslas expresiones en pequenos rompecabezas. A pesar detodo, C ha
demostrado ser un lenguaje extremadamente eficaz y expresivo.
Este lenguaje ha evolucionado paralelamente a UNIX, que a su vez
hapasado por diversas versiones entre las que destaca ladeMicrosoft con
su XENIX para micros de 16bits.
En esteapartado sevan a exponer los pasos a seguir en la realizaci6n de
un programa, por medio deun ejemplo. Lasiguiente figura, representa es-
tos pasos en el orden en el que hay que ejecutarlos.
El ejemplo delafigura indica queuna vezeditados los ficheros fuente
a.cyb.c, son compilados obteniendose los ficheros objeto a.obj yb.obj los
cuales son enlazados con el fichero c.obj, con lalibreria d.lib y con las li-
brerias del sistema .lib dando lugar a un unico fichero ejecutable a.exe.
Laordencorrespondiente para compilar yenlazar losficherosexpuestos
en este ejemplo, es la siguiente:
Para ejecutar el fichero a.exeresultante, 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 0procesador de textos que vayamos a utilizar. Podemos
utilizar el procesador de textos suministrado con el compilador 0nuestro
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 10hasta ahora expuesto, escribir el si-
guiente ejemplo:
CAPITULO I: INTRODUCCION AL LENGUAJ E 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 %s\n%s\n': mensajel, nombre, mensaje2);
J
Comentamos brevemente cada linea de esteprograma. 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 seescribe la funci6n principal main( ). Las dos pri-
merasHneasdeesta, definen lascadenas decaracteres mensajel ymensaje2
y la siguiente linea define la cadena de caracteres nombre para contener
49caracteres mas el caracter de "fin de cadena" que aftade C automati-
camente.
Lafunci6n printj( ) escribeel contenido delas 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 enlamemoria. Para que estetrabajo pue-
da tener contir.uidad, el programa escrito sedebe grabar en el disco utili-
zando la orden correspondiente del editor.
El siguiente paso es compilar el programa, esto es, traducir el programa
fuente alenguaje 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, senos pueden presentar errores de compi-
lacion, debidos aqueel programa escrito no seadapta alasintaxis y reglas
del compilador. Estos errores seiran corrigiendo hasta obtener una com-
pilacion sin errores.
Cada vez que serealiza 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 .exedespues del prompt
del DOS y pulsando Enter a continuacion.
Cuando secreaun fichero ejecutable, C utiliza primero el compilador
para compilar el programa fuente, dando lugar aun fichero intermedio co-
nocido como fichero objeto (.obj). A continuacion C utiliza el programa
Iink.exe para unir, enun unico fichero ejecutable, el modulo 0los modulos
del programa compilados separadamente y las rutinas delas 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 seejecuta el programa y senin notifi-
cados con el correspondiente mensaje de error.
Hay otro tipo de errores queno dan lugar amensaje alguno. Por ejem-
plo: un programa que no termine nunca de ejecutarse, debido a que pre-
senta un lazo, donde no sellega a dar la condicion determinacion. Para
detener la ejecucion setienen que pulsar las teclas Ctrl +C.
Un programa una vez ejecutado puede dar lugar auna solucion incorrec-
ta. Estecaso exigeun analisis minucioso decomo sedesarrolla 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, seexplica como utilizar el de-
purador Code View de Microsoft.
Vamosapreparar un programa formado par un solo modulo fuente, para
depurarlo. El primer paso seraeditar el programa. Como ejemplo escribit
el siguienteprograma, e1cual, imprime el valor ysucuadrado, decada uno
deloselementos deuna matriz numerica de5filas y 3columnas, ycuenta
eimprime 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 enlafunci6n principal, main(), vemos quedefinimos una ma-
triz cuadrada cca': para acontinuaci6n recorrerla elemento aelemento por
filas.
Esta funci6n principal llama alafunci6n display( ) que escribe el va-
lor del elemento objeto deanalisis y sucuadrado, y llama tambien alafun-
cion numero_par( ) que indica si el numero es 0no par. En el caso de
que sea par incrementamos en una unidad el contador numeros_pares.
Una vezque sehan analizado todos los elementos delamatriz, sees-
cribe el numero de valores pares encontrados.
Como siguiente paso, compilaremos el programa con las opciones IZi
y IOd.
Laopcion IZi hace que seincluya en el fichero ejecutable resultante,
informacion necesaria para realizar ladepuracion ylaopcion IOd impide
la optimizacion, la cual puede dificultar la depuracion.
Cuando finaliza el proceso decompilacion 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 seejecuten 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 0hasta 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 sesos-
peche que esta el error.
Para poner 0quitar 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 0de expresiones del programa, mientras este seejecuta (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 0FIO. Si se pulsa la tecla
F5, la ejecuci6n del programa continua hasta el final del pro-
grama 0hasta 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 laextension quepuede tener hasta tres carac-
teresy vaseparada 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
queconellosdebehacersepara construir el programa final.
Cuando seespecifica el nombre deun fichero sinextensi6n, 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 0mas 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 0CR +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, 10cual nos permite escribir programas mas legibles.
EI caracler Ctr! +Z bajo DOS, (equivalente a Ctrl +0bajo UNIX) es
tratado POl' el compiIador como un indicador de fin de fichero (End Of File).
Loscaracteres tambien pueden ser representados por secuencias deescape.
Una secuencia de escape esta formada por el caracter \ seguido de una
letra 0deuna combinaci6n de digitos. Son utilizadas para acciones como
nuevalinea, tabular y para representar caracteres no imprimibles.
\n
\t
\v
\b
\r
\f
\a
\'
Nueva linea
Tab horizontal
Tab vertical (s610para impresora)
Backspace (retroceso)
Retorno de carro
Alimentaci6n de pagina (s610para 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 f 1oat .h especifican los valores maximo y minima para cada 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, 10que 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 0negativo. 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 0a 127 son equivalentes a un caracter.
De forma similar el tipo unsigned char puede almacenar valores en
el rango de 0a 255, valores correspondientes a los numeros ordinales de
10s256 caracteres ASCII.
Este ejemplo declara una variable car de tipo char, capaz de contener
un canicter cuyo c6digo ASCII secorrespondeni 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 0a 65535 (0a 2EI6-1).
Este ejemplo declara i yj, como variables enteras con posibilidad de
tDmar va\Dres entre -'3216'il y '1'2161. Otros ejemp\os 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 ( 0a 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 (0a 2E32-1) para el tipo unsigned long.
Este ejemplo declara las variables n y m detipo entero, pudiendo to-
mar valores entre -2147483648y 2147483647. Otros ejemplos son:
long a =-IL;
signed long b =125;
unsigned long int c = Oxlj00230j;
Ladeclaraci6n deun tipo enumerado es simplemente una lista devalores
quepueden ser tornados por una variable deesetipo. Los valores del tipo
enumerado serepresentanin 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 0m,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 Iepuede asignar un valor ini-
cial por medio de una expresi6n constante. 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 0escribir 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-38para numeros negativos
1.175494E-38 a 3.402823E+38 para numeros positivos
Un numero real en simple precision no tiene mas de 7digitos signifi-
cativos.
Esteejemplo declaralavariablex detipo real ensimpleprecision. Otros
ejemplos son:
float a = 3.14159;
float b =2.2e-5;
Un numero real endoble precision esun numero quepuede 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 16digitos significati-
vos. Esto da lugar a calculos mas exactos que en simple precision.
Esteejemplo declara lavariablex detipo real endobleprecision. 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 seutiliza para dec1arar funciones que no retornan un valor
opara dec1arar un puntero aun tipo no especificado. Si void aparece entre
parentesis a continuacion del nombre deuna 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 dec1arala funcion denominada jx, como una funcion
sin argumentos que devue1veun valor de tipo real de doble precision; la
funcionjy, como una fundon sinargumentos que no devuelvevalor 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 10que normalmente cono-
cemos como registro, esto es, un conjunto de uno 0mas 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, adiferencia de las estructuras, representan registros variables. Esto cjuiert:
decir que una variable de este tipo, puede alternar entre varios tipos.
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 40elementos (!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 0derivados, 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 deint; el tipo
PFI como un puntero auna funci6n quedevuelveun 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 devuelveun valor entero y per como una estructura 0registro
de tipo persona.
Las declaraciones typedej permiten parametrizar un programa para
evitar problemas deportabilidad. Utilizando typedej conlostipos quepue-
den depender delainstalaci6n, cuando selleveel programa aotra instala-
ci6n s610setendnin que cambiar estas declaraciones.
Una constante esun 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 0una cadena de caracteres.
En general, si la constante es positiva, el signa +es opcional y si es
negativa, llevael signo -. El tipo deuna constante entera vienedetermina-
do por su valor. Tambien sepuede indicar explicitamente el tipo de una
constante entera, afiadiendo los sufijos L, U, 0UL (mayusculas 0minus-
culas). Si el sufijo esL, sutipo eslong cuando el valor puede ser represen-
tado enestetipo, si no esunsigned long. Si el sufijo es U, sutipo esunsig-
ned int cuando el valor puede ser representado enestetipo, si no esunsigned
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 0mas digitos, 0a 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, 0a7, precedidos por
o (cera). Su valor esta comprendido en el rango:
Oa 077777
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, 0a 9 y
A a F, precedidos por Ox 0OX (cero mas x). Su valor esta comprendido
en el rango:
OxOa Ox7FFF
Ox8000a 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
O x J O O
-0400
-O x J O O
especifica el nO256 en decimal
especifica el n 256 en octal
especifica el nO256 en hexadecimal
especifica el nO-256 en octal
especifica el nO-256 en hexadecimal
Una constante real estaformada por una parte entera, seguidapor un punto
decimal, yuna parte fraccionaria. Tambien sepermite lanotaci6n cientffi-
ca, en cuyo caso seafiade al valor una e0E, seguida por un exponente
positivo 0negativo.
donde dfgitos representa cero 0mas digitos del 0al 9yE 0e esel simbolo
de exponente de la base 10que puede ser positivo 0negativo (2E-5 =
2xJ(f5). Si la constante real espositiva 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 seafiada
a la misma una f 0F, en cuyo caso sera de tipo float, 0una I 0L para
indicar que es de tipo long double.
Estetipo deconstantes esta formado por un unico caracter encerrado en-
trecomillas simples. Una secuencia deescapeesconsiderada 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 decaracteres 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 aquelacadena "0pul-
sar Enter" se escriba en una nueva linea.
Cuando una cadena decaracteres esdemasiado larga puede utilizarse
el canicter " \ " como canicter de continuaci6n.
HEsta cadena de caracteres es dema \
siado larga."
Dos 0mas cadenas separadas por un espacio enblanco sedan conca-
tenadas en una sola cadena.
printf(HPrimera cadena,"
Hsegunda cadena ");
Los caracteres deuna cadena decaracteres sonalmacenados enlocali-
zacionessucesivasdememoria. Cadacadenadecaracteresesfinalizada auto-
m<iticamentepor el caracter nulo representado por lasecuencia deescape
\ o.
El tipo deuna cadena de caracteres es el tipo array donde cada ele-
mento es detipo char (char [ J) y laclase dealmacenamiento esstatic. El
numero de elementos deun array, necesarios para almacenar una cadena
decaracteres, esigual al numero decaracteres delacadena, mas uno para
el caracter nulo de terminaci6n.
Los identificadores son nombres dados aconstantes, variables, tipos, fun-
ciones y etiquetas deun programa. La sintaxis para formar un identifica-
dor es la siguiente:
10cual indica que un identificador consta de uno 0mas caracteres (letras,
digitos y el caracter de subrayado) y que el primer canicter debe ser una
tetra 0el cardcter de subrayado.
Las letras pueden ser mayusculas 0minusculas 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 esuna secuencia decaracteres utilizada para explicar el co-
digo fuente. Microsoft C soporta comentarios estilo C y estilo C++.
Un comentario estilo C esuna secuencia decaracteres 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 delalinea. Estos comentarios no pueden ocupar mas deuna linea.
Por ejemplo:
Un comentario puede aparecer en cualquier lugar donde sepermita
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
10largo de la ejecucion de un programa.
La sintaxis correspondiente a la declaraci6n de una variable es la si-
guiente:
c1ase representa una delas cuatro clases siguientes: auto, register, sta-
tic, 0extern. La clase deuna variable determina si esta tiene ca-
racter global (static 0extern) 0local (auto 0register).
Una variable declarada fuera detodo bloque (conjunto desentencias
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 deun bloque, espor defecto local y esaccesible solamente
dentro de este.
Cada variable deun programa, debedeclararse antes deser utilizada.
La declaraci6n consiste en enunciar el nombre de la variable y asociarle
untipo. El tipo determina losvalores quepuede tomar lavariable 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, sepuede 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 Iepuede 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 10mismo con el puntero.
const char *pe = "abed";
pe[Oj ='z'; / * error */
pe = "efg"; / * eorreeto */
Si 10que sepretende es declarar un puntero como una constante, 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'; herror */
pe = "efg"; / * error */
A ladeclaraci6n de un objeto, sepuede anteponer el calificador volatile,
con el fin de hacer que dicho objeto pueda ser modificado por otros pro-
cesosdiferentes al programa actual. Suutilizaci6n tiene sentido, por ejem-
plo, en procesos concurrentes. Los calificadores const y volatile, pueden
utilizarse conjuntamente 0individualmente.
Este ejemplo declara lavariable v, entera (int), accesible desde cual-
quier parte (extern), no modificable por el programa donde estadeclarada
(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 10sda-
tos. Sepueden clasificar en 10ssiguientes grupos: aritmeticos, 16gicos, re-
lacionales, unitarios, 16gicospara manejo debits, deasignaci6n, operador
ternario para expresiones condicionales y otros.
Division. Losoperandos pueden ser enteros 0reales. Si am-
bos operandos son enteros el resultado esentero. En el res-
to de los casos el resultado es real.
Modulo 0resto deuna 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 3de tipo int */
/ *el resultado es 1de tipo int */
/ * el resultado es 3de tipo into Se
convierte a float para asignarlo a y */
AND. Da como resultado el valor logico 1si ambos ope-
randos son distintos de cero. Si uno de ellos es cero el re-
sultado esel valor logico O.Si el primer operando esigual
acero, el segundo operando no es evaluado.
OR. El resultado es0si ambos operandos son O. Si uno de
los operandos tiene un valor distinto de 0, el resultado es
1. Si el primer operando esdistinto decero, el segundo ope-
rando no es evaluado (ASCII 124).
NOT. EI resultado es0si el operando tieneun valor distin-
to de cero, y 1en caso contrario.
EI resultado es detipo into Los operandos puedenser enteros, reales
a punteros.
p&& q
p I I q
!p
da como resultado 0
da como resultado 1
da como resultado 0
Una expresi6n de Boole da como resuItado IosvaIores I6gicos 001. 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 designoaI operando (compIemento ados). EI ope-
rando puede ser entero 0real.
CompIemento a 1.EI operando tiene queser entero (canlc-
ter ASCII 126).
Los operandos para este tipo de operaciones tienen que ser de tipo en-
tero (char, int, long, 0enum), 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 1todos los bits de a que */
/ * estdn a 1en 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=a1;
d=b1;
/* 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 */
hrealiza 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 */
Ctieneunoperador ternario (?:), queseutiliza enexpresiones condiciona-
les, las cuales tienen la forma:
Laexpresi6noperandol debeser detipo entero, real 0puntero. Laeva-
luaci6n serealiza de la siguiente forma:
Si el resultado delaevaluaci6n deoperandol esdistinta de0, 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.
Esteejemplo diceque si a >b entonces mayor =a, en caso contra-
rio, mayor =b.
Unpar deexpresiones separadas por una coma son evaluadas deizquierda
aderecha. Todos los efectos delaexpresi6n delaizquierda son ejecutados
antes de evaluar la expresi6n de la derecha, a continuaci6n el valor de la
expresi6ndelaizquierda esdescartado. 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 aun campo de bits perteneciente auna estructura 0aun 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 0de
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 esun identificador 0un nombre deun tipo dedatos. Los
parentesis son opcionales, exceptocuando laexpresi6n secorresponde con
un tipo de datos.
"primera cadena':
"segunda cadena':
"tercera cadena"
];
const int cadenas = (size of cadena)/(sizeof cadena[Oj);
En esteejemplo, cadena representa un array depunteros aobjetos de
tipo char. Puesto queel numero deelementos (punteros) no seha espeeifi-
cado, una forma faeil decalcularlo esmediante laoperaci6n que seexpre-
saacontinuaci6n. El resultado sedeposita encadenas que ha side defini-
da como una constante (const).
Latabla que sepresenta a continuaci6n, resume las reglas de priori dad y
asociatividad de todos los operadores. Los operadores escritos sobre una
mismalinea tienen lamisma prioridad. Las lineas sehan colocado dema-
yor a menor prioridad.
Una expresi6n entre parentesis, siempre seevalua primero. Losparen-
tesistienenmayor prioridad ysonevaluados demas internos amas externos.
() [ ] ->
*
& ++
:>
*
/
0 7 0
+

< <= > >=
--
I -
.-
&
I I .
Cuando los operandos dentro deuna expresi6n son detipos diferentes, se
convierten a un tipo comun, de acuerdo con las reglas que seexponen a
continuaci6n.
Las reglas que se exponen, se aplican en ese orden, para cada opera-
cion binaria 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 0unsigned 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 serealiza la multiplica-
cion, desputs la division y por ultimo la suma, sedesarrollarfa de la forma
siguiente:
2. c es convertido a unsigned int (paso 8). Seejecuta la multiplica-
ci6n (*) y se obtiene un resultado de tipo unsigned into
4. El resultado deb * c, esconvertido adouble (paso 3). Seejecuta
la divisi6n (I) y se obtiene un resultado de tipo double.
5. a esconvertido adouble (paso 3). Seejecuta lasuma (+) Yseob'-
tiene un resultado de tipo double.
6. El resultado de a + b * c / d, para ser asignado a1, es pasado
aenteropor truncarniento, estoes, eliminando laparte fraccionaria.
Los operandos que intervienen enuna determinada operaci6n, son
convertidos al tipo del operando de precisi6n mas alta.
En una asignaci6n, el valor delaparte derecha esconvertido al tipo
del valor delaparte izquierda, deacuerdo con las siguientes reglas:
Los caracteres seconvierten aenteros con 0sinextensi6n desig-
no, dependiendo esto delainstalaci6n. Bajo Microsoft C lacon-
versi6n se hace con extensi6n de signo.
Losenteros seconvierten acaracteres preservando losbits deme-
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 pasa afloat, 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 cada 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 0unsigned 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
Lavariable atributo esuna estructura delongitud ocho bits. Si desea-
mos copiar atributo enuna variable atrib detipo char, seguramente escri-
biriamos:
char atrib;
atrib =(char}atributo;
10cual 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 detipo sobre punteros, es posible
convertir el valor deuna variable deun determinado tipo aotro tipo cual-
quiera. El formate general para llevar esto a la pnictica es:
char *atrib;
atrib = (char *}&atributo;
define la variable a detipo 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 enlos ficheros .h. Algunos deestos tipos y sus definiciones,
son los siguientes:
c1ock_t estetipo esta definido en time.h y es utilizado por la fundon
clock( ).
estetipo esta definido en stdio.h yesutilizado por las funcio-
nesjgetpos( ) y jsetpos( ).
estetipo esta definido enstdio.h y en otros ficheros .h. Es un
tipo entero sin signo, resultado del operador sizeo!
estetipo 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:
-30C
-24 C
-22.00F
-11.20F
90C
96 C
194.00F
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" Iedice 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" hjichero estdndar de c; que se incluye e.n
* el program a
* /
/ *Dejinicion de constantes */
#dejine INF -30 hlimite injerior de la tabla de temperaturas d
#dejine SUP 100 / * limite superior */
Una declaraci6n introduce uno 0mas 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, 0cuando 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 sedesea, pueden ser inicia-
lizadas en la propia declaracion.
La definicion de una variable, declara la variable y ademas Ieasigna
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 0la 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) 0a 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 esuna combinaci6n deoperadores y operandos que dan lu-
gar a un unico valor.
Una sentencia eslaunidad ejecutable mas pequefia deun programa C. Las
sentencias controlan el flujo uorden deejecuci6n. Una sentencia C consta
deuna palabra clave(for, while, if ...else, etc.), expresiones, declaraciones,
o llamadas a funciones.
Dos 0mas sentencias pueden aparecer sobreuna misma linea, separa-
das por punta y coma.
Una sentencia compuesta 0bloque, esuna 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
Unafunci6n esuna colecci6n desentencias queejecutan una tarea especi-
fica. Una funci6n no puede contener a otra funci6n.
Puesto quelasfunciones sonuna herramienta muy valiosa enlacons-
trucci6n deun programa C, vamos aanticiparnos adescribir c6mo sede-
claranysedefinen, con el fin depoder utilizarlas desdeel primer momen-
ta en la programaci6n. Posteriormente se estudianin con mas detalle.
Ladeclaraci6n de una funci6n, tambien conocida como funcion prototi-
po, consiste en:
Seobserva, que enladeclarad6n delafund6n, sedan sus caracteris-
ticaspero no sedefine sucontenido. Una fund6n puede ser declarada im-
plicitamente 0con una declaracion forward (fund6n prototipo).
Ladeclaracion implicita sedacuando lafund6n esllamada yno exis-
teuna declarad6n previa (declaraci6n forward). En estecaso, C, por de-
fecto, construye una fund6n prototipo con tipo de resuItado int y la lista
detipos de argumentos seconstruye, en base a los parametros formales
espedficados en ladefinici6n dela fund6n. Esto obliga a que el tipo del
resultado en la definici6n de la fund6n sea into
Ladeclaracion explicita, permite conocer lascaracteristicas delafun-
ci6n antes de ser utilizada.
LaIista de tipos de argumentos normal mente consiste en una lista de
identificadores con sus tipos, separados par comas. En el caso deuna fun-
ci6n prototipo, sepueden 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, sepuede 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, 0tipo definido por el usuario,
perono puede ser un array 0una funci6n. Por defecto, el valor retornado
esinto Este valor es devuelto a la sentencia de Hamada, por medio de la
sentencia:
Esta sentencia puede ser 0no laultima, ypuede aparecer mas deuna
vezen el cuerpo de la funci6n. En el caso de que la funci6n no retorne
un valor, se omite.
Lospanimetros formales deuna funci6n son lasvariables quereciben
losvaloresdelos argumentos enlaHamada alafunci6n; consisten enuna
listade 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 0valores 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 sepodia haber escrito utilizando
el estilo antiguo, de la siguiente forma:
float conversion (cent) / *cabecera de fundon */
int cent;
{
Cuando seHamaa una funcion, el valor del primer panimetro actual es
pasado al primer panimetro formal, el valor del segundo panimetro actual
espasado al segundo panimetro formal yas! sucesivamente. Todos los ar-
gumentos, excepto los arrays, son pasados por valor. Esto es, alafuncion
sepasa 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 sedesea poder alterar los contenidos de los argumentos en la Ha-
mada, entonces hay que pasarlos por referencia. Esto es, a la funcion, se
pasa ladireccion del argumento yno suvalor por 10queel 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 ey, 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 ey, indirectamente estamos modificando los valores
dea y b respectivamente. Recordar la definici6n de puntero ylas definicio-
nes de los operadores de indireccion (*) y direccion-de (&).
Un programa C puede ser dividido en uno 0mas ficheros fuente. Un fiche-
ro fuente C, es un fichero de texto que contiene todo 0parte de un progra-
ma C.
Para compilar un programa formado por varios ficheros, sedeben 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-
cespara el compilador, deciaraciones y definiciones. Pero, un elemento como
una funci6n 0una 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:
Sedenomina ambito de una variable (scope) a la parte de un programa
donde dicha variable puede ser referenciada por su nombre. Una variable
puedeser limitada aun bloque, aun fichero, auna funci6n, 0auna decla-
raci6n de una funci6n prototipo.
Hay cuatro clases de ambito: local, global 0fichero, funci6n y es-
tructura.
Cuando una variable sedeclara fuera detodo bloque en un programa, es
accesibledesde su punta de definici6n 0declaraci6n hasta el final del fi-
chero fuente. Esta variable recibe el calificativo de global.
Una variable global existeytiene valor desde el principio hasta el fi-
nal delaejecuci6n 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 ladeclaraci6n deuna variable sehacedentro deun bloque, el acce-
soadicha variable queda limitado aesebloque yalos bloques contenidos
dentro deestepor debajo desu punta dedeclaraci6n. En estecaso, lava-
riable recibe el calificativo de local 0automatica.
Una variable local existey tiene valor desde su punta dedeclaraci6n
hasta el final del bloque donde esta definida. Cada vez que el control se
pasaal bloque para su ejecuci6n, las variables son nuevamente definidas;
ycuando finaliza laejecuci6n del mismo, lasvariables dejan deexistir. Un
elementocon caracter local esaccesiblesolamente dentro del bloque al que
pertenece.
EI siguiente ejemplo muestra el ambito de las variables, dependiendo de
si estan definidas en un bloque 0fuera de todo bloque.
En esteejemplo, al tratar dedefinir el ambito deuna variable, distin-
guimos cuatro niveles:
Las variables definidas enestenivel, 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]); hescribe ]00 */
l/*FINAL DEL BLOQUE ] */
printf("%d \ n': var]); / * escribe 50 */
l/*FINAL DE main( ) Y DEL PROGRAMA */
Lasvariables definidas enestenivel, solamente son accesibles des-
delapropia funci6n main( ) y, por 10tanto, son accesibles en los
bloques 1y 2.
Las variables definidas en este nivel, solamente son accesibles en
el interior del bloque 1y, por 10tanto, en el bloque 2.
Las variables definidas en estenivel, solamente son accesibles en
el interior del bloque 2.
En el ejemplo anterior seobserva 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
accesibilidaddelalocal 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 deuna funci6n, son locales ala funci6n; y 10sparametros de-
clarados enlalista deparametros deladeclaraci6n deuna funci6n proto-
tipo, tienen un ambito restringido a la propia declaraci6n de la funci6n.
El nombre de un campo 0miembro de una estructura es local a la
mismay puede ser utilizado solamente: despues del operador "." aplicado
a una variable del tipo de esa estructura, 0despues del operador "- >"
aplicado aun puntero aesaestructura. En loscapitulos sucesivos, al tratar
lasestructuras y punteros a estructuras, veremos esto con mas detalle.
Por defecto, todas lasvariablesllevanasociada una clasedealmacenamiento
que determina su ac.cesibilidad y existencia. Los conceptos de accesibili-
dady deexistenciatanto 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 0register pueden ser utilizados solamente con
variables locales; el calificador extern puede ser utilizado solamente con
variables globales 0funciones; y el calificador static puede ser utilizado
con variables locales, globales 0fund ones.
En una variable dec1aradaanivel externo, esto es, fuera detoda definicion
defundon, sepueden utilizar loscalificadores static 0extern 0bien omitir
el calificador. A nivel externo no sepueden utilizar los calificadores auto
o register.
Una variable dec1aradaanivel externo esuna definicion delavariable
o una referencia a una variable definida en otra parte. Esto quiere decir
que ladec1aracion deuna variable externa inicializa lavariable acero, por
defecto, 0a un valor especificado.
Una variable definida anivel externo, puede ser accesible antes desu
definicion, 0en otro fichero, utilizando el calificador extern.
Esto quiere decir, que lautilizacion del calificador extern tiene senti-
do cuando lavariable ha side definida anivel externo, una vez, y solamen-
.teuna, en cualquier parte del programa y queremos tener acceso aellaen
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. Ladeclaraci6n extern en el segundo fichero, permite acceder ala
variable var en este fichero.
5. Si la variable var no hubiera sido inicializada explfcitamente, C
Ieasignaria automaticamente el valor O.
Si se utiliza el calificador static en la declaraci6n de una variable a
nivel externo, estasolamente esaccesibledentro desupropio fichero fuen-
te. Esto permite declarar otras variablesstatic conel mismo nombre enotros
ficheros correspondientes al mismo programa.
En una variable declarada a nivel interno, esto es, dentro de un bloque,
sepueden utilizar cualquiera delos cuatro calificadores, u omitir el califi-
cador, encuyocaso seconsidera lavariablecomo auto (local 0automatica).
Una variable declarada como auto solamente esvisibledentro del blo-
que donde esta definida. Este tipo devariables no son inicializadas auto-
maticamente, por 10que hay que inicializarlas explfcitamente, cuando sea
necesario.
Una variable declarada anivel interno como static, solamente esvisi-
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 esinicializada solamente una vez, cuan-
do comienza laejecuci6n del programa. No esreinicializada cadavez que
seejecuta el bloque que lacontiene. Si lavariable no esinicializada expli-
citamente, C la inicializa automaticamente a O .
Una declaraci6n register indica al compilador quelavariable sera al-
macenada, si es posible, en un registro de la maquina, 10que producini
programas mas cortos ymas rapidos. EI numero deregistrosutilizables para
estetipo devariables, depende delamaquina. Si no esposible almacenar
una variable register en un registro, selada el tratamiento deautomatica.
Este tipo dedeclaraci6n esvalida para variables detipo int y detipo pun-
tero, debido al tamafio del registro.
Una variable declarada como register solamente es visible dentro del
bloque donde esta definida. Este tipo devariables no son inicializadas auto-
maticamente, por 10que 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 0modulo en el cual no 10es.
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 esteejemplo, lavariable var] esdefinida anivel externo. En lafun-
cion main( ) seutiliza una declaracion extern, para hacer accesible dentro
de esta, la variable var]. La variable var2 declarada static es inicializada,
por defecto, a O.
En lafuncion denominadafuncion~ sedefine lavariable local varl,
anulando asi alavariable xterna var]. Lavariable var2, declarada static,
esinicializada a5. Esta definicion no entra enconflicto con lavariable var2
delafuncion main( ), yaquelasvariables static anivel interno son visibles
solamente dentro del bloque donde estan declaradas. A continuacion la
variable var2 es incrementada en 5, de tal forma que si funcion~ fuera
Hamada otra vez, el valor inicial para esta variable seria de 10, yaque las
variables internas declaradas static, conservan susvalores deuna ejecucion
a otra del bloque.
Una fundon declarada static esaccesiblesolamentedentro del fichero fuente
en el que esta definida.
Una funcion declarada extern esaccesibledesdetodos losficheros 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 setrate depresentar lasintaxis correspondiente auna 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;
Lasentencia deasignacion esasimetrica. Esto quiere decir que laex-
presiondeladerecha es evaluada, y el resultado es asignado a lavariable
especificadaalaizquierda. Deacuerdo con estadefinicion, no serfavalida
lasentencia:
Si lavariable es de tipo puntero, solamente sela puede asignar una
direccionde memoria, la cual sera siempre distinta de O. Un valor 0(se
escribeNULL) sirvepara indicar que esavariable puntero no apunta aun
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 aestas, par 10que debera contener lalinea:
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, 10es-
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, 0un 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 seuti-
liza junto con + entonces se ignora.
cuando seutiliza con la especificaci6n de formate 0, x, 0X,
antepone al valor de salida 0, Ox, 0OX 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, 0G,
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 delas especificadas, el ancho
es incrementado en 10necesario.
Si el ancho y/o laprecision seespecifican con el caracter *,
el valor para estos campos setoma del siguiente argumento
entero.
int ancho =15, precision =2;
float valor= 12.345;
printj((% *. 4'~ancho, precision, valor);
(double) valor con signo dela forma: [-]dddd.dddd. El nu-
mero dedigitos antes del punta decimal depende delamag-
nitud del numero y de lacantidad de decimales dela preci-
sion, la cual es 6por 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 seespecifica %Fp 0%Ip seescribe
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 1aprecisi6n especifica e1numero de digitos que tienen que
ser escritosdespuesdel punto decimal. E1valor esredondeado.
Por defecto es 6.
g,G 1aprecisi6n especifica e1maximo numero dedigitos signifi-
cativos (6por defecto) que setienen que escribir.
s 1aprecisi6n especifica e1maximo numero decaracteres aser
escritos. Loscaracteres queexcedanestenumero, seignoran.
F y N son una amp1iaci6n de Microsoft C, por 10que no
pertenecen a1C estandar.
seutiliza como prefijo con 10stipos d, i, 0, x, y X, para es-
pecificar que e1argumento esshort int, 0con u para especi-
ficar un short unsigned into
seutiliza como prefijo con 10stipos d, i, 0, x, y X, para es-
pecificar quee1argumento eslong int, 0con upara especifi-
car un long unsigned intoTambien seutiliza con 10stipos e,
E, f, g, y G para especificar un double en 1ugar deunfloat.
seuti1izacomo 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 deentrada. Esta formado por caracteres
enblanco : \ t, \ n), caracteres ordinarios yespecificacio-
nes de formato. El formato selee de izquierda a derecha.
Cada argumento debetener sucorrespondiente especificaci6n
de forma.to y en el mismo orden.
Si un caracter en la entrada estandar no secorresponde con
la entrada especificada por el formato, seinterrumpe la en-
trada de datos.
argumento es un puntero a la variable que se quiere leer.
~ando seespecificamas deun argumento, losvalorescorrespondientes
enlaentrada hay que separarlos por uno 0mas espacios en blanco :
\ t, \ n) 0por el caracter que se especifique en el formato.
Unespacio enblanco antes 0despues deuna especificaci6n deforma-
tohacequescanf( ) 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
Especificacionesdeformato queno incluyan espacios enblanco 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 asterisco a continuacion del simbolo 070 suprime la asigna-
cion del siguiente dato en la entrada.
maximo numero decaracteres aleer delaentrada. Los caracte-
res en exceso no son tenidos en cuenta.
F y N son una ampliacion deMicrosoft C, por 10que no perte-
necen al C estandar.
h seutiliza como prefijo con 10stipos d, i, n, 0, y x, para especifi-
car que el argumento es short int, 0con u para especificar un
short unsigned into
seutiliza como prefijo con 10stipos d, i, n, 0, y x, para especifi-
car que el argumento es long int, 0con 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 0como 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, 16u 8. Si el entero co-
mienza con 0se toma el valor en octal y si empieza
con O x 0 O X el valor se toma en hexadecimal.
en el entero es almacenado el numero de caracteres
leidos del buffer 0del 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 OJ oc, 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 OJ ospor
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 J avier
Resultado: Francisco J avier
Observar que nombre no lleva el operador de direcci6n &, por tratarse
deun 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 selea 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, 0un EOF si se detecta el final
del fichero 0si 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, seobtiene 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,
10que d~.;ugar aun bucle infinito; jjlush(stdin) borra el contenido del buffer
asociado con la entrada estandar, 10que permitira que scanj solicite un
nuevo dato de la entrada.
Cuando seteclea Ctrl +Z (0Ctrl +D en UNIX) seactiva 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, seencontrara 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, 10que nos conduce de nuevo a un bucle
infinito; clearerr(stdin) desactiva el indicador de fin de fichero de la entrada
estandar, 10que permitira que getchar( ) soli cite 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 0combinaci6n 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 0si 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 128bytes, 0no 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 deuna cantidad c, invertida aun interes rOJ o 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 vi enen dad as por
la expresi6n:
-b sqrt(b
2
- 4*a*c)
2* a
# include "stdio.h
n
# 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, 10que indica
que no hay raices reales sino complejas. Para dar soluci6n a este proble-
ma, ver las sentencias de control en el pr6ximo capitulo.
Tomauna decision referente alaaccion aejecutar enun programa, basan-
doseen el resultado (verdadero 0falso) de una expresion.
if(expresion)
sen ten cial;
[else
sentencia2] ;
expresion debe ser una expresion numerica, relacional 0logica. El
resultado que seobtiene al evaluar la expresion es verda-
dero (no cero) 0falso (cero).
sentencial12 representan una sentencia simple 0compuesta. Cada sen-
tenciasimpledebeestar separada delaanterior por unpun-
to y coma.
1
Si el resultado de la expresion es verdadero, seejecutani 10indicado
por lasentencial.
Si el resultado de la expresion es falso, seejecutani 10indicado por
la sentencia2.
Si el resultado delaexpresion esfalso, ylaclausula else sehaomitido,
la sentencia1 seignora.
En cualquier caso, laejecucion continua conlasiguientesentencia eje-
cutable.
if (x)
b=a/x;
b=b+1;
En este ejemplo la expresion es una expresion numericax. Entonces
b =a / x, que representa lasentencia1, seejecutara si laexpresion esver-
dadera (x distinta de 0) y no seejecutara si la expresion es falsa (x igual
a 0). En cualquier caso, se continua la ejecucion en la linea siguiente,
b=b+J.
En esteotro ejemplo, laexpresion a <b esuna expresion derelacion.
Lasentencia c =c +1, solo seejecutara si a esmenor queb. Si a esmayor
o igual que b, secontinua en la linea siguiente, ignorandose la sentencia
c=c+J.
if (a && b)
x =i;
En esteejemplo, laexpresion a &&b esuna expresion logica. Lasen-
tencia x =i solo seejecuta si a y b son distintos de cero. En otro casa,
la sentencia x =i seignora.
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-
tanlas 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;
Lasentencia break-se-ejecutani solamente cuando car sea igual al ca-
racter 's:
Lassentencias if ...else pueden estar anidadas. Esto quiere decir que como
sentencial 0sentencia2, 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, IaregIapara diferenciar cada una de estas sentencias, es que cada
else secorresponde 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 esteejemplo aparecen dos sentencias if anidadas. Aplicando Iare-
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 sepasa a ejecutar la siguiente
lineadeprograma. Si 10que sedesea es que seejecutes = 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 decomo resultado el menor detres 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 acontinuaci6n, aparece con bastante frecuencia
, y:es por 10que Iedamos un tratamiento por separado. Esta estructura es
, ,consecuencia de las sentencias if anidadas .
.' . S:-': ,:,: ..
(i~ t 2;,:;,<
if (expresionl)
sentencial;
else if (expresion2)
sentencia2;
else if (expresion3)
sentencia3;
else
sentenciaN
Si secumpIela expresionl, seejecuta lasentencial y si no secumple
seexaminan 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, seejecuta lasentenciaN corres-
pondiente aelse. En cualquier caso, secontinua en lasentencia que sigue
a la estructura.
Al efectuar una compra en un cierto almacen, si adquirimos mas de
100unidades deun mismo articulo, nos hacen un descuento deun 400/0,
entre 25 y 100un 20070, entre 10y 24 un 10% y no hay descuento para
una adquisici6n de menos de 10unidades. 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,
estetiene que ser duplicado en la expresi6n correspondiente. Como ejem-
plo, observar en el ejercicio anterior el formato %9d%%: %9d es el for-
matoutilizado para escribir el tanto por ciento de descuento y %% es para
escribir a continuaci6n el canicter "%':
Estasentencia 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 deun solo canicter 0una
expresion constante; entodos los casos, el valor resultante tiene
que ser entero.
Al principio del cuerpo delasentencia switch, pueden aparecer decla-
raciones. Las inicializaciones, si las hay, son ignoradas.
Lasentencia switch evalua laexpresion entre parentesis ycompara su
valor con las constantes de cada case. La ejecucion de las sentencias del
cuerpo delasentencia switch, comienza enel case cuyaconstante coincida
con el valor delaexpr-test ycontinua hasta el final del cuerpo 0hasta una
sentencia que transfiera el control fuera del cuerpo (por ejemplo break).
La sentencia switch puede incluir cualquier numero de c1<iusulascase.
Si no existeun valor igual al valor delaexpr-test, entonces seejecutan
las sentencias acontinuacion dedefault, si estac1<iusulahaside especifica-
da. Lachiusula default puede colocarse encualquier parte del cuerpo yno
necesariamente al final.
Leer una fecha representada por dos enteros, mes y ano y dar como
resuitado Ios rlias correspondientes al mesoTener en cuenta que Febrero puede
tener 28 029 dias si el ano es bisiesto. Un ano es bisiesto cuando es muIti-
pIa de 4 y no de 1000cuando 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) I I (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 finalizala ejecuci6n deuna sentencia do, for, switch, 0while
en la cual aparece.
Cuando estas sentencias estan anidadas, lasentencia break solamente
finaliza la ejecuci6n de la sentencia donde esta incluida.
EI siguiente ejemplo calcula el importe apagar por un vehiculo al cir-
cular por una autopista.
Seutiliza un tipo enumerado. Lasvariables deun tipo enumerado son
tratadas como si fueran de tipo into A cada elemento de un tipo ordinal
seIeasignael numero deorden partiendo del O.Estenumero deorden 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 0compuesta, cero 0mas 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 sepasa aejecutar lasiguiente sentencia enel programa.
3. Si el resultado de la expresi6n es distinto de cero (verdadero), se
ejecutalasentencia yel procesoserepitecomenzando enel punto 1.
Larutina siguiente solicita obligatoriamente una delas dos respuestas
posibles: sin (S10no).
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 dacomo resultado lasuma deuna seriedecan-
tidades introducidas por teclado. Laentrada dedatos 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 1y 50, que cumplan la expresi6n:
/ * Cuadrados que se pueden expresar
* como suma 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;
Ejecutauna sentencia, simple0compuesta, una 0mas veces, dependiendo
del valor de una expresi6n.
do
sentencia;
while (expresion);
3. Si el resultado de la expresion es cero (falso), sepasa 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 laraiz cuadrada deun numero n, por el metoda deNewton
quedice:
# 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
Coeficientedeerror: le-6
Cuando sedesea ejecutar una sentencia simple 0compuesta, repetidamen-
teunnumero deveces conocido, laconstrucci6n adecuada es lasentenciafor.
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, seevalua la expresi6n que da lugar alaprogresion
de la condicion y se vuelve al punta 2.
2.2 Si el resultado de 2 es cero (falso), la ejecuci6n de la senten-
ciafor se da por finalizada y se continua en la siguiente sen-
tencia del programa.
for (i = 1; i <= 100; i ++)
printf("%d ': i);
Esteejemplo imprimelosnumeros dell aliOO. Literalmentedice: desde
i igual a 1, mientras i sea menor 0igual 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 0con Ctrl +C.
En generallas sentencias repetitivas while, do, y for sepueden colocar in-
distintamente unas dentro de otras para formar bucles anidados.
Un bucle puede colocarse dentro deotro bucle y entonces sediceque
estan anidados. En estecaso el bucle interno seejecutara total mente, cada
vez que se ejecute el bucle que 10contiene.
Escribir un programa que imprima un triangulo construido con ca-
racteres consecutivos del c6digo ASCII, como el que se muestra a conti-
nuaci6n.
#
0 J 0 &
( )
# 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 sepuede mover un alfil desde una posicion dada.
2. Partiendo delafila 1, columna 1y recorriendo por filas el tablero
imprimir un(a):
* si secumple, quelasuma 0diferencia delafilay columna ac-
tuales, coincide con lasuma 0diferencia delafila 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) I I
(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, 0for, pasa el
control para que se ejecute la siguiente iteraci6n.
El siguiente programa imprime todos los numeros entre 1y 100que
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 seabandona, iniciandose la ejecuci6n del mismo para un nuevo valor
de n.
Lasentencia goto transfiere el control auna lineaespecifica del programa,
identificada por una etiqueta.
Si lalinea alaque setransfiere el control esuna sentencia ejecutable,
seejecuta esasentencia y las queIesiguen. Si no esejecutable, laejecuci6n
seinicia en la primera sentencia ejecutable que seencuentre a continua-
ci6n de dicha linea.
No sepuede transferir el control fuera delafunci6n enlaque 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, seutiliza
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 e1valor 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 = 0y 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 0mas espacios (' '), carac-
teres tab (\ t) 0caracteres 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 ==" I I car =='\t' I I 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 yel numero de lineas del mis-
mo. Suponemos que una palabra esta separada de-otra por uno 0mas es-
pacios (' '), caracteres tab (\ t) 0caracteres 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 ==" I I car
palabra = NO;
else
if (palabra ==NO)
[
/ * contador de caracteres */
== '\ n' I I 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 <1I I 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 0aparentemente aleato-
riadenumeros, sellamaun generador denumeros 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 lasecuencia r
k
,
escalculado apartir desupredecesor rk-l' utilizando lasiguiente formula:
La secuencia as! generada, es Hamada mas cprrectamente secuencia
pseudoaleatoria, yaquecada 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 _2
31
a 2
31
- 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 0y 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 0y 1. Para este
proposito podemos utilizar una version modificada como la que seexpone
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
enun cubo C. Puede demostrarse que laprobabilidad deque 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 a1azar, 1aprobabi1idad de que este se
encuentre tambien dentro del sector esferico es:
Por 10tanto, para saber e1vo1umende1aesfera, basta calcu1aresapro-
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
Unarray esuna estructura homogenea, compuesta por varias componen-
tes, todas del mismo tipo y almacenadas consecutivamente en memoria.
Cadacomponente puede ser accedido directamente por el nombre de la
variablearray seguido deuno 0mas subindices encerrados entrecorchetes.
La representacion de los arrays sehace mediante variables suscritas
o desubindices y pueden tener una 0varias dimensiones (subindices). A
losarrays deuna dimension seles llama tambien listas; y alos de dos di-
mensiones, tablas.
Desdeel punta devista matematico, en mas deuna ocasion necesita-
remosrepresentar variables, tales como:
all a
l2
a
l3
a
21
a
22
a
23
si seutilizan dos subindices. Para realizar esta misma representaci6n bajo
C, tendremos que recurrir a los arrays que acabamos de definir y quea
continuaci6n se estudian.
Por ejemplo, supongamos que tenemos un array unidimensionailla-
made datos, el cual contiene tres elementos. Estos elementos seidentifica-
reinde 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 dedos dimensiones serepresenta mediante una variable con
dos subindices (filas, columnas); un array detres dimensiones serepresen-
tamediante una variable con tres subindices etc. El numero maximo dedi-
mensiones 0el 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.
t ipo nombre [t amafio};
t ipo nombre [ ];
tipo indica el tipo de 10selementos del array. Puede ser cualquier
tipo excepto void.
tamaiio esunaconstante queespecificael numero deelementosdel array.
EI tamafio puede omitirse cuando seinicializa el array, cuando
sedeclara como un panimetro formal enuna funcion 0cuando
se hace referencia a un array declarado en otra parte del
programa.
Esteejemplo declara una variable array denominada lista con 100ele-
mentos (del 0al 99), cada uno de ellos de tipo into
Esteejemplo declara una variable array denominada nombre con 40
elementos(0a 39), cada uno de ellos de tipo char.
Este ejemplo declara el tipo y el nombre de un array de punteros a
objetosdetipo char. La definicion actual de vector sehace en otra parte
del programa.
Unelemento deun array sepuede 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 quepara referenciar un elemento deun array sepuede emplear
comosubindiceuna constante, una variable 0una expresiondetipo entero.
tipo nombre [expr-cte][expr-cte] ...;
tipo nombre [ ][expr-cte] ...;
La primera expr-cte puede omitirse cuando seinicializa el array, cuando
se declara como un panimetro formal en una funcion 0cuando 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[Oj a b[1lj[4j), eini
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 0expresio
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 0mas subindices, dependiendo de las dimensio
nes del mismo, cada uno de ellos encerrado entre corchetes. Un subindice
puede ser una constante, una variable 0una expresion cualquiera.
Algunos compiladores de C, no permiten inicializar arrays de una 0
mas dimensiones si estan declarados como locales (aut o). 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 0declarandolo 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 einicializa 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 20al ele-
mento y[1][9J y el valor 30al 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 sehace atraves 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 3columnas de la forma:
( 168 ENCICLOPEDIA DEL LENGUAJ E C
Realizar un programa que asigne datos auna matriz unidimensional
a den 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 auna matriz t de dos dimen-
siones y, acontinuaci6n, 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
Rea1 izar un programa que a1 macene en una matriz 1 0stantos por ciento
deaprobados correspondientes a 1 0scursos de BUP en 1 0salios 1 986a 1 990
y, a continuaci6n, permita consu1 tar dicha matriz.
/ * Tanto por ciento de aprobados en los cursos 1 ~2a y 3a
* de BUP en los afios 1 986 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 cua1 quiera */
d o
{
system(Hcls");
do
[
printj("Curso que desea consultar (1, 2 03) ");
scanj("%d': &curso);
1
while (curso <1 I I curso >3);
printjt' \ n ");
do
[
printj("Ano (%d a %d) ': AC, AF);
scanj("%d': &anno);
1
while (anno <AC I I 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 devalores. A continua-
cion, y sobrelalista, encontrar losvaloresmaximo 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");
Unacadena decaracteres esun array unidimensional, enel cual todos sus
elementosson de tipo char.
Un array de caracteres puede ser inicializado asigmindole un literal.
Por ejemplo:
Esteejemplo inicializael array decaracterescadena concincoelementos
(cadena[Oj acadena[4/). El quinto elemento, esel canicter nulo (\ 0), can
el cual C finaliza todas las cadenas de caracteres.
Si seespecifica el tamafio del array decaracteres ylacadena asignada
esmas larga queel tamafio especificado, seobtiene un error enel momen-
to de la compilaci6n. Por ejemplo:
Este ejemplo daria lugar aun mensaje deerror, indicandonos quehe-
mos excedido los limites del array.
Si lacadena asignada esmas corta queel tamafio del array decaracte-
res, el resto delos elementos del array son inicializados avalor 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 deca-
denas decaracteres. Esto es, UsIa esun array de 100filas, cadauna delas
cuales es una cadena de caracteres de longitud maxima 60.
Antes deleer una cadena decaracteres, debedeclararse el array detipa
char que lavaacontener. Ladimensi6n deestearray debe corresponderse
con el numero de caracteres maximo que puede contener la cadena, mas
uno correspondiente al caracter nulo determinaci6n. Por ejemplo, si que-
remos leer un nombre de40 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 esuna direccion, ladireccion decomienzo
del array. Si se lee elemento a elemento, el operador &es necesario.
Lafunciongets( ) leeuna lineadelaentrada estandar, stdin, ylaalmacena
enlavariable especificada. Esta variable esun puntero alacadena deca-
racteresleida.
Lavariable var, contiene todos los caracteres tecleados, excepto el ca-
racter nuevalinea ( \ n), que es automciticamente reemplazado por el ca-
dcter nulo (\ 0), con el cual C finaliza toda cadena de caracteres.
Lafuncion gets( ) devuelve un puntero al valor leido. Un valor nulo
paraestepuntero, indica un error 0una condicion defin defichero (eo!).
Lafuncion gets( ), a diferencia de la funcion scanf( ), permite la en-
tradadeuna cadena de caracteres formada por varias palabras separadas
par espacios en blanco, sin ningun tipo de formato. Recordar que para
scanf( ), el espacio enblanco actua como separador dedatos enlaentrada.
Lafuncionputs( ) escribe una cadena de caracteres en la salida estandar
stdout, yreemplaza el caracter nulo determinacion delacadena ( \ 0) por
el caracter nueva linea (\ n).
Lafundon puts( ) retorna un valor positivo si seejecuta 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:
leenlos datos requeridos delaentrada estandar referendada par stdin. Es
necesario tener presente quelos datos, cuando sontecleados, no son leidos
directamente del dispositivo, sino queestos son depositados enuna memo-
ria intermedia (buffer), asociada con el dispositivo de entrada. Los datos
sonleidos delamemoria intermedia cuando sonvalidados; estoocurrecada
vez que pulsamos Enter.
Recordar que las funciones scanf( ) y getchar( ) interpretan el caracter
, \ n' (nuevalinea) como un separador. En modo texto y bajo DOS, pulsar
lateclaEnter equivaleatransmitir dos caracteres: CR + LF (estosenUNIX
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 esun canicter valido para la funcion
gets(), entre otras; 10que quiere decir que cuando estas funciones secom-
binan enun mismo programa, hay problemas delecturas no deseadas. La
solucionaesto eslimpiar el buffer asociado constdin despues deuna lec-
turacon las funciones scanj( ) 0 con getchar( ), y antes deuna lectura con
lafuncion 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 nOentero y un nOreal: \ 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");
f or (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 decaracteres. Segun 10expuesto en el parrafo anterior, enel buffer aso-
ciado por el sistema astdin queda el caracter LF; acontinuaci6n, esteca-
racter sera leido par lafunci6n gets( ), sustituyendo asi, ala primera cade-
na que tendria que leer esta funci6n~Este problema seevitalimpiando el
buffer destdin conlafunci6nfflush(), antesderealizar lalecturacongets( j.
Con lafunci6n getchar( ), nos ocurre el mismo problema queconscanf( j.
Examinar una cadena decaracteres. Leer una cadena decaracteres 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
laentrada y que de como resultado, la linea leida y su longitud 0numero
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
* t \0'), 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
enunarray decaracteres. 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 quecuando dimensionamos un array decaracteres, por ejem-
plo aun valor deLONG-.MAX, los indices delos elementos disponibles
van desde0 aLONG-.MAX-1. En el ejemplo vemos que si laentrada ex-
cedelalongitud maxima establecida, lacadena setrunca y sefinaliza con
caracter nulo (' \ 0'), convenio utilizado por C.
Un array de cadenas de caracteres es un array donde cada elemento esa
su vez un array decaracteres. Dicho deotra forma, es un array de dos di-
mensiones de tipo char.
El siguiente ejemplo, leeuna 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");
f or (n = 0; n <i; n ++)
printft'%s \ n': lista[n));
Lasdeclaraciones delas funciones, para manipular cadenas decaracteres,
queacontinuacion sedescriben, estan en el fichero a incluir string.h. La
sentencia:
Estafundon afiade lacadena2 alacadenal, termina lacadena resultante
conel caracter nulo y devuelve un puntero a cadena!.
Esta funci6n devuelveun puntero ala primera ocurrencia dec 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 copialacadena2, incluyendo el canicter determinaci6n nulo,
en la cadenal y devuelve un puntero a cadenal.
Esta funci6n dacomo resultado laposici6n (subindice) del primer canicter
decadenal, quepertenece al conjunto decaracteres contenidos encadena2.
Estevalor corresponde alalongitud delasubcadena decadenal formada
por caracteres no pertenecientes acadena2. Si ningun canicter decadena!
pertenece acadena2, el resultado eslaposici6n del canicter determinaci6n
( \ 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 ncaracteres decadena2 alacadenal, ter
mina la cadena resultante con el canlcter nulo y devuelve un punteroa
cadenal. Si n es mayor que la longitud decadena2, seutiliza como valor
de n la longitud de cadena2.
Esta funci6n compara losprimeros ncaracteres decadenal ycadena2, 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, setoma 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
esmenor quelalongitud decadena2, no seaiiade automaticamente unca
racter nulo alacadena resultante. Si nesmayor quelalongitud decadena2
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, seefectua 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-
bienpueden 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 enel
espacio dememoria asignado y devuelveun puntero alacadena copiada.
Estafuncit'>ndacomo resultado el mensaje deerror correspondiente al nu-
merodeerror oro_error. Este numero deerror normalmente viene dado
por lavariable predefinida errno.
Conviertelas letras mayusculas decadena, en minusculas. El resultado es
lapropia cadena en minusculas.
Conviertelas letras minusculas decadena, enmayusculas. El resultado es
lapropia cadena en mayusculas.
Estafunci6n sustituye todos los caracteres decadena, por el canicter c, ex-
ceptoel canicter de terminaci6n \ O.
Cuando las funciones atof( ), atoi( ) y atol( ) leen un canicter que
esreconocido como parte deun numero, dejan deleer caracteres dela\
riable cadena.
# include <stdio.h >
# include <stdlib.h >
main( )
!
char *cadena = "-123.45E+05";
double r;
r = atof(cadena);
printf("%E': r);
l
Convierteun numero real auna cadena decaracteres. Lacadena decarac-
teresserafinalizada con el caracter nulo. Esta funci6n devuelveun punte-
ro ala cadena de caracteres.
decs numero dedigitos despues del punta decimal. Si esnecesario,
seafiaden ceros.
pdec devuelveun 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 auna cadena decaracteres. Lacadena decarac-
teresserafinalizada con el caracter nulo. Esta funci6n devuelveun punte-
roalacadena de caracteres.
es la base (2- 36) en la que viene especificado el valor. Si la
base es 10y 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.
Lacadena decaracteres 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 10y 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
Lasdeclaraciones para las funciones, que acontinuaci6n sedescriben, para
clasificar y convertir caracteres, estan en el fichero aincluir ctype.h ( #include
<ctype.h .
Comprueba si c es un canicter de control (0 - 31y 127).
int iscntrl(int c);
Comprueba si c es un digito ('0' - '9').
int isdigit(int c);
Comprueba si cesun 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);
Todasestas funciones devuelven un valor distinto de 0 si sesatisface
lacondici6n, y 0 en caso contrario.
Ponea0todos los bits dec, excepto los 7bits demenor orden. Dicho de
otraforma, 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");
}
}
}
Unaestructura es un nuevo tipo deIdatos que puede ser manipulado de
lamismaforma quelostipos predefinidos comofloat, int, char, entreotros.
Unaestructura sepuede definir como una coleccion dedatos dediferent~s
tipos, logicamente relacionados. En C una estructura solo puede contener
dec1aracionesde variables. En otros compiladores, estetipo de construc-
danes son conocidas como registros.
Crearuna estructura es definir un nuevo tipo de datos, denominado tipo
estructura y declarar una variable de este tipo. En la definicion del tipo
estructura, seespecifican los elementos que lacomponen as! como sus ti-
pas. Cada elemento delaestructura recibeel nombre demiembro (campo
del registro). La sintaxis es la siguiente:
struct tipo_estructura
{
declaraciones de los miembros
};
Despuesdedefinir un tipo estructura, podemos declarar una variable
deesetipo, 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
cada 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
nopuedeser inicializado. Sutipo puede ser: fundamental, array, puntero,
union, estructura 0funcion.
Para declarar un miembro como una estructura, es necesario haber
declaradopreviamente esetipo deestructura. En particular una estructura
st no puede contener un miembro de tipo st, pero sf puede contener un
puntero 0referencia 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.
Losmiembros deuna estructura son almacenados secuencialmente, en
el mismo orden en el que son declarados.
Con una variable declarada como una estructura, pueden realizarse lassi-
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 0array de registros. Esta es una cons-
trucci6n muy uti! y potente.
El siguiente programa leeuna lista dealumnos ysus correspondientes
notas definal decurso, dando como resultado el tanto por ciento dealum-
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 \ n
H
);
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( HclsH);
f or (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
deuna 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 dedefinir un tipo uni6n, podemos declarar una 0mas varia-
bles de esetipo de la forma:
Para referirse aun determinado miembro delauni6n, seutiliza lano-
taci6n:
Para almacenar los miembros deuna uni6n, serequiere una zona de
memoria igual a la que ocupa el miembro mas largo de la uni6n. Todos
los miembros son almacenados enel mismo espacio dememoria ycomien-
zan en la misma direcci6n. El valor almacenado es sobreescrito cadavez
que se asigna un valor al mismo miembro 0 a un miembro diferente.
union tipo_union
{
char varl;
int var2;
float var3;
};
Esteejemplo declaraunavariablevar_union querepresentaunaunion
con tres miembros. Esta variable debe ser 10suficientemente grande como
para contener el mayor delos tres miembros. Cualquiera delos tres miem-
bros pueden asignarse avar_union yutilizarse enexpresiones. Es respon-
sabilidad del programador recordar cual esel miembro quehay enlaunion.
El esquema siguiente, expresa c6mo sevegraficamente var_union.
var_union
I
I I I
E
varl -l
~
.1
var2
var3
Si en lugar dehaber 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, 0bien los datos edicion
y anno, 0bien nomrev.
Consideremos una biblioteca compuesta por libros yrevistas. 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(
H
Edici6n ");
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 0funciones 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 sealinea 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 8a 12 */
unsigned : 2; / * bits 13a 14de 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 seha explicado anteriormente, podemos acceder alos miem-
bras individuales de la estructura utilizando el operador pun to, como se
veen 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 <=c
z
' - ca'; j++)
for (e = 0; e <= c
z
' - ca'; e++)
tablaU][e] =0;
system ("cls ");
printf("Introdueir texto. Finalizar con AZ. \ n \ n ");
carant = c';
while ((car = tolower(getehar( ))) != EOF)
I
if ((earant >= ca' && earant <= C
z
') &&
(car> = ca' && car <= C
z
'))
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 <=c
z
'; 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 0con una combinacion de estas opciones.
Bajo MS-DOS, la memoria intermedia de pantalla con adaptador mo-
nocromo es de 4000bytes, localizados a partir de la direccion 0del seg-
mento OxBOOO.La memoria intermedia de pantalla con adaptador color
graficos (CGA) es de 4000bytes de longitud en modo texto y de 16384bytes
de longitud en modo gnifico, localizados, en ambos casos, a partir de la
direccion de memoria 0del 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 (111en binario).
# include <stdio.h >
# include <stdlib.h >
# include <ctype.h >
YLas 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 hultima 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 4a 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:
lacual indica, que la funci6n escribir debe recibir dos valores de tipo char:
caryatributo. 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:
lacual 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: 1000001 0 E =3
Mantisa: 01010000000000000000000 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.Mx2
E
-
127
para 0<E<255.
Aplicado a nuestro ejemplo, obtenemos:
Un puntero es una variable que contiene la direccion de memoria, de un
dato 0de otra variable que contiene al dato. Quiero esto decir, que el pun-
tero apunta al espacio ffsico donde esta el dato 0la 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, *- C j ;
/ *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 16bits 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, senos 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 - qes valida. No se permite sumar, multiplicar, divi-
dir 0rotar 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 aelementos del mismo array.
if (p +n <= q)
P += n;
if (q /= NULL &&q <= p)
q++;
Losoperadores 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.
lavariable b sedecrementa enuna unidad. Sinparen-
tesis, se decrementaria pb y no b.
Un ejemplo interesante depunteros esun examen del contenido dela
memoria. El siguiente programa permite visualizar byte abyte el conteni
do de la memoria RAM, a partir de una posici6n dada.
Este programa introduce lapalabra clavefar lacual nos proporciona
una direcci6n segmentada, con lafinalidad depoder acceder aposiciones
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 ej emplo 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. Spara sa!ir ");
r = getch( );
J
while (tolower(r) 1= 's'); / * j ina!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
detipo unsigned char ya que el rango de valores para este tipo es de 0a255.
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 yarrays, una relaci6n tal que, cualquier opera-
ci6n que sepueda realizar mediante la indexaci6n de un array, sepuede
realizar tambien con punteros.
Para clarificar 10expuesto, 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 aesta direccion Iesumamos 1, 0dicho 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]
esla 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
quela del primer elemento. Por otra parte, despues de haber efectuado la
asignacion p = /ista, las siguientes expresiones dan lugar a identicos re-
sultados:
Sinembargo, 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 J avier";
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 seencuentra.
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, sepasa ala siguiente direcci6n y serepite
el proceso desde el punta 1.
Puesto que la sentencia while s610comprueba si laexpresi6n *p 1=t \ 0'
escera, en lugar de hacer la comparaci6n explfcitamente, se puede hacer
implicitamente as!:
while (*p)
P++;
Ahara la condici6n esta formada por la expresi6n *p. Si esta expre-
si6nesdistinta 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
seriavalida 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 decaractereS enotra. Prime-
ramente sepresenta 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) != <\ O J
[
p++;
q++;
J
J
Si tras1adamos e1incremento dep y q a 1aexpresi6n de 1asentencia
while, obtenemos esta otra versi6n:
void copstr(char *P, char..q) / * copia q en p */
[
while ((*P+ + = ..q+ +)!= <\ 0');
J
E1bucle formado por 1asentencia while, realiza 1assiguientes opera-
ciones:
1. Tomae1canicter contenido en 1adirecci6n dadapor q y 10copia
en 1adirecci6n dada por p.
3. p pasa aapuntar a 1asiguiente direcci6n, y 10mismo sucede con
q. Si no sedio 1acondici6n de terminaci6n, e1proceso serepite
desde e1punta 1.
Puestoque1asentenciawhile s610comprueba si 1aexpresi6n *P !=< \ 0'
escera, en 1ugar de hacer 1acomparaci6n explicitamente, sepuede hacer
implicitamenteasi:
void copstr(char *P, char..q) / * copia q en p */
[
while (*P ++ = ..q ++);
J
Cuando sepasa el nombre deun array auna funci6n, sepasa ladirec-
ci6n de comienzo del array, ya que el nombre del array es un puntero a
dicho array; dicho deotra forma el paso deun array sehacepor referencia
y no por valor.
Para hacer referencia auna cadena decaracteres, podemos utilizar un pun-
tero 0 un array.
Cuando seutiliza un puntero, podemos inicializar lavariable delafor-
ma siguiente:
La version array asigna el numero suficiente debytes 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 seefectua de la misma
forma, pero ademas seasigna memoria para lavariable puntero nombre,
que esla que contiene la direcci6n decomienzo dela cadena. Esto quiere
decir que con estaversi6n seocupa mas memoria, pero tambien las venta-
jas son mayores.
En laversi6n array, nombre es un puntero constante; por 10 tanto, la
direcci6n no puede ser modificada. En laversi6n puntero, nombre esuna
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.
Sepuede definir un array, para que sus elementos contengan en lugar
de un dato, una direcci6n 0 puntero.
int *a[lO j, v;
a[O ] = &v;
prin!f("%d': *-fl[O]);
Este ejemplo define un array de 10elementos 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, Ieda 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 ][lO j;
int *b[lO j;
Este ejemplo define un array a de 100 elementos de tipo entero y un
array b de 10elementos declarados como punteros a objetos de tipo ente-
rooSuponiendo que cada uno de estos objetos es un array de 10elementos,
laocupaci6n de memoria sera de 100elementos mas la de los 10elementos
de b.
Las ventajas que tiene el array b sobre el a, es que el acceso a un ele-
mentoseefectua mediante una indirecci6n atraves de un puntero, en lugar
dehacerlo mediante una multiplicacion yuna suma, yquelosarrays apun
tados pueden ser de longitud variable.
Para especificar que una variable esun puntero aun puntero, sepro
cede de la forma siguiente:
El siguiente programa multiplica los elementos deun array dedosdi
mensiones por una constante y acontinuacion escribe el contenido dedi
cho array. Primeramente sepresenta una version con arrays ydespues 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 dela 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 todo el array es tabla. Este array
tiene4elementos (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 aun puntero, 0 que
el contenido de tabla +1, *(tabla +1), es la direcci6n del primer elemento
deesa 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 aestudiar mediante un ejemplo, como inicializar un array depun-
teros a cadenas de caracteres. Consideremos el problema de escribir una
funcion para que recibiendo un numero entero correspondiente aun 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': "J unio': "J ulio':
':4gosto': HSeptiembre': HO ctubre':
HNoviembre': HDiciembre" J ;
return ((mm > 0 &&mm <=12) ? mes[mmj : mes[O J );
J
En este ejemplo, mes[ j es un array de 13elementos (0 a 12) que son
punteros acadenas de caracteres. Estas, como seve, 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
dedos 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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * O rdena las cadenas pcadena[O J ... 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. Laclasificacion y, por consiguiente, laescritura del resultado, se
realizasobre el array de punteros, ya que resulta mas facH.
Existendos metodos fundamentales que C utiliza para almacenar infor-
macionenlamemoria. El primero utiliza variables globales y locales. En
el casodevariables globales, el espacio es fijado y utilizado a 10 largo de
todalaejecucion del programa (~ATA); yen el caso devariables loca-
les, la asignacion sehace a traves del stack.
El segundo metodo utilizafunciones predefinidas enC, como mal/ocr )
yfree(). Como eslogico, estas funciones utilizan el area dememoria 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, envez dehacerlo enel momento delacompilacion del mismo.
Cuando seasigna memoria para un objeto de un tipo cualquiera, sede-
vuelve un puntero a la zona de memoria asignada.
Lafunci6n mal/oc devuelveun puntero quereferencia el espacio asig-
nado, a un objeto de un tipo no especificado. Si hay insuficiente espacio
dememoria 0 si tes 0, la funci6n retorna un puntero nulo (NULL).
El espacio puede ser asignado acualquier tipo deobjeto. Para realizar
----""laconversi6nal tipo deseado, utilizar lanotaci6n cast sobreel valor devuelto.
El siguiente programa asigna espacio para cadenas decaracteres. 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 e1puntero 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 deejecuci6n, denominados tambien arrays
dimimicos.
scanj("O /O d': &n_elementos);
a =(iot *)mal/oc(n_elementos * sizeof(iot));
for (i =0; i < n_elementos; i ++)
scan!("O /O d': &a[i]);
Estafunci6n asigna espacio dememoria para un array den elementos, de
longitud t bytes cada uno de ellos. Cada elemento es inicializado a o.
Lafunci6n calloc( ) devuelveun puntero al espacio asignado. Estees-
pacio puede ser asignado a un array de cualquier tipo. Para ello, utilizar
lanotaci6n cast sobre el valor devuelto.
Si hay insuficiente espacio de memoria, 0 si not son 0, la funci6n
retornaun puntero nulo (NULL). EI espacio de memoria requerido debe
ser inferior a 64K.
EI siguiente programa asigna espacio para un array de N elementos
detipo 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( ) 0por la propia funci6n rea-
l/oc( ). EI bloque ha podido, 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 aun objeto de cualquier tipo.
Para ello, utilizar la notaci6n cast sobre el valor devuelto.
Si hay insuficiente espacio de memoria 0si tes 0, la funci6n retorna
un puntero nulo (NULL). Cuando esto ocurre, el bloque original es liberado.
Estafunci6n libera un bloque dememoria asignado por las funciones ma-
lloc( ), calloc( ) 0 realloc( ). Un puntero nulo es ignorado.
EI siguienteprograma muestra c6mo realizar una reasignaci6n deme-
moria; y pone demanifiesto que despues deuna reasignaci6n, lainforma-
cionnovaria en el espacio dememoria conservado. Por ultimo, el bloque
dememoria 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 se r
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 dememoria asignado por la funcion haUoen
#include <malloc.h>
void hfree(void _huge *p);
Lospunteros aestructuras sedeclaran igual que los punteros aotros tipos
dedatos. C utiliza el operador - >para referirse aun miembro deuna es-
tructura apuntada por un puntero.
EI siguiente ejemplo declara un puntero hoy auna estructura, asigna
unvalor acadamiembro delamisma y, apoyandose enuna fundon, escri-
be su contenido.
# include <stdio.h >
# include <stdlib.h >
struct jecha / * 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 deun ope-
rador (array: [ ], puntero: *, 0funci6n: ( ) ). Sepueden 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 deinterpretar declaraciones complejas esleerlas 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 0parentesis 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 esteejemplo sehan numerado los pasos enel orden deinterpreta-
cion, que es como sigue:
Nota: Para una informacion avanzada sobre punteros ver el capitulo titu-
lado "Manejo de la memoria".
Unafuncion esuna coleccion independiente dedeclaraciones ysentencias,
generalmenteenfocadas arealizar una tarea especifica. Todo programa C
constaal menos deuna funcion, lafuncion main( ). Ademas deesta, pue-
dehaber otras funciones cuyafinalidad es, fundamentalmente, descompo-
nerel problema general ensubproblemas mas faciles deresolver ydeman-
tener. La ejecucion de un programa comienza por la funcion main( ).
Cuando sellama a una funcion, el control sepasa a la misma para
suejecucion; ycuando finaliza, el control esdevuelto denuevo al modulo
quellamo, para continuar con la ejecucion del mismo a partir de la sen-
tenciaque 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 estadefinida; 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 aun
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, 0un tipo definido por el usuario. Por defecto es
into
Sino se pasan argumentos a /a /Line.ion, /a usta de pa.afmetros J O rma-
les puede ser sustituida por la palabra clave void
Si esnecesario, el compilador ejecuta las conversiones aritmeticas usua-
lessabre cada parametro formal y sobre cada argumento actual.
Esteejemplo define una funci6n Hamada suma, que acepta dos valo-
senteros y retorna un valor entero.
Esteejemplo 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 detipo entero (int) y de clase de almacenamiento register y p que
unpuntero aun valor de tipo char. EI resultado devuelto por la funci6n
unvalor 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 aun 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 deuna funcion esta formado por una sentenciacompuesta que
contienesentendas quedefinen 10quehacelafundon. Tambienpuede con-
tener declaraciones de variables utilizadas en dichas sentendas. Estas va-
riables, por defecto, son locales a la fundon.
Hemosvisto quecada funcion puede devolver un valor cuyo tipo seindica
enlacabeceradefuncion. Estevalor esdevuelto alasentenda deHamada
ala fundon, por medio delasentenda return, cuyasintaxis eslasiguiente:
Si lasentenda return no seespecifica 0seespecifica sincontener 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 deexpresiones 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 asus correspondientes panimetros fOf'
males. El numero de expresiones en la lista, debe ser igual al
numero de parametros formales, a no ser que seespecifique un
numero variable de argumentos.
Este ejemplo llama a la funcion suma( ) y Ie pasa 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 Iepasa los valores de
a y b. La funcion devuelve un valor que es almacenado en la variable r.
Esteejemplo llama ala funci6n mayor( ) y Iepasa los valores de v[i}
y dev[i+1] (elementos del array v). La funci6n devuelve un valor que es
almacenado en la variable n.
Cuando seejecuta una llamada a una funci6n, el control es pasado
alamisma para su ejecuci6n. La ejecuci6n dela funci6n finaliza cuando
seejecuta una sentencia return 0 cuando sellegaal final del cuerpo dela
funci6n. En esteinstante, el control es devuelto al punta dellamada para
continuar la ejecuci6n del programa.
Ladeclaraci6n de una funci6n, denominada tambien funcion prototipo,
permiteconocer el nombre, el tipo del resultado, los tipos delos panime-
trosformales yopcionalmente susnombres. No define el cuerpo delafun-
cion.Estainformaci6n permite al compilador chequear lostipos delospa-
nimetros actuales por cada llamada a la funci6n. Una funci6n no puede
serHamadasi previamente no esta definida 0declarada. A esta regIahay
unaexcepci6n 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 ala llamada. Si
estonosucedeasi, entonces es necesario declarar la funci6n antes deque
Una funcion prototipo tienelamisma sintaxis queladefinicion deuna
funcion, excepto que esta termina con un punta y coma.
..A~~.)lamada. La dec1aracion deuna fundon puede ocurrir a nivel interno
':(j" anivel 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 auna fundon precede asudefinicion, yno existeuna
declaracion forward delamisma, implidtamente estaseconstruye contipo
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 AZ. \ n \ n");
while (scanj("%f': &!ista[i+ +J ) !=EO F && i < N)
clearerr(stdin); / * desactivar el indicador EO F */
/ * 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
laejecuci6n de una sentencia scanf(). Esta entrada finaliza cuando sepul-
sen las teclas etr) +Z (F6), 10que hace que se active el indicador EOF de
fin de fichera. Este indicador queda activado mientras no se desactive ex-
plicitamente, 10que 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 aun 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 I I 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;
case3: 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), enel buffer delaentrada estandar stdin queda el
caracter '\ n' (separador para scanf( )). Como esun caracter valida para
unafuncion como gets( ), es necesario limpiar este buffer, antes de que
seejecuteuna funcion como esta. Esta operacion serealiza mediante la
funcionjjlush(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) I I (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-%I
J
\ 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
ensuscorrespondientes parametros formales, operaci6n quesehace auto-
m<iticamentecuando sellama auna funci6n, con 10cual no semodifican
losparametros actuales.
Pasar parametros por referencia, significa que 10 transferido no son
losvalores, sino lasdirecciones delasvariables queconti enenesosvalores,
can 10 cual los parametros actuales pueden verse modificados.
Cuando sellama a una funci6n, los argumentos especificados en la
Hamadason pasados por valor; excepto los arrays que sepasan por refe-
rencia, ya el nombre del array es un puntero a dicho array.
Utilizando laforma depasar parametros por valor, pueden ser trans-
feridasconstantes, variables y expresiones; y utilizando la forma depasar
parametros por referencia, solamente sepermite transferir las direcciones
devariables de cualquier tipo, arrays y funciones.
Para pasar una variable por referencia, sepasa ladirecci6n del para-
metro actual asucorrespondiente parametro formal, el cual tiene que ser
unpuntero. Para ello, utilizar el operador &antes del nombre delavaria-
ble. Para pasar ladirecci6n deun array 0 deuna funci6n, no esnecesario
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;
LaHamada alafunci6n sumar( ), pasaaesta funci6n los panimetros
4, v y v*2-1 por valor y el panimetro suma 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 sevemodificada, a pesar dehaber variado b, yaqueha
sido pasada por valor.
Para pasar todos los elementos de un array a una funci6n, sepone enla
listadepanimetros actuales el nombre del array, que esladirecci6n deco-
mienzo del array; yenlalistadepanimetros formales, el nombre del array
seguido de sus dimensiones. De estas, como ya dijimos, sepuede 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;
f or if = 0; j <ji/as; j ++)
f or (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(), leeuna linea detexto ycalcula sulongitud.
Si resulta queestalineaeslamas larga delasleidas hasta ahora, lafundon
copiar( ) la guarda en el array linea_max, para al final escribirla.
Un puntero igual que otros tipos devariables 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 deasignar un valor alas estructuras
apuntadas por a y por b, utilizando una funci6n. Esta fund6n tiene dos
parametros formales, correspondientes alos parametros actuales a yb, los
cualesson: p puntero a un objeto detipo pareja y q que es un puntero a
unpuntero aun objeto detipo pareja. En base a10 expuesto, a espasado
por valor y b es pasado por referenda.
Lafunci6njuncion.-X(), primeramente asigna memoria para un ob-
jeto detipo pareja y 10 deja apuntado por p. Este objeto no es visible en
main(), yaqueenlallamada alafund6n 10 quepasamos ap, fueel valor
dea. Ahora p, ha tornado un nuevo valor que no es reflejado en a.
A continuad6n seasigna memoria para otro objeto de tipo pareja,
el cual queda apuntado por q. Este objeto esvisible en main( ), ya que q
referendaab, por 10 quetodo cambio efectuado sobre *q, severatambien
reflejado sobre b.
Otro detalle que cabe resaltar, es lautilizaci6n delafunci6n strepy( )
para copiar una cadena enotra. Seguramente algunos intentarian resolver
esta cuesti6n utilizando, por ejemplo, la sentencia:
char a[20], b[20] = "abed";
a = b; / * error */
El error que seobtiene esdebido aquea esuna constante, por 10 que
su valor no es modificable. En cambio si realizamos las declaraciones:
char ~, *b ="abed";
I =b; / * eorreeto */
el resultado escorrecto, yaquea esuna variable. En estecaso estamos asig-
nando e1valor de la variable puntero b, a: la variable puntero a. Aharaa
y b apuntan a la cadena "abed': La funci6n strepy( ) copia contenidos,
no direcciones.
Cuando seejecuta un programa C, la primera funci6n que sellama
para su ejecuci6n es main( ). En general, el formate para esta funci6nes:
En muchasocasiones cuando invocamos un programa desdee1sistemaope-
rativo, necesitamos escribir uno 0 mas argumentos acontinuaci6n del nom
bre del programa, separados por blancos.
En esteejemplo, progQl esel programaqueejecutamos y-n esel argumen-
to aprocesar inmediatamente, bajo esteprograma. Lacadena "-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 aun argumento, demanera que: argv[O]contie-
neel nombre del programa, argv[1]el primer argumento delali-
nea de ordenes, argv[2] el segundo argumento, etc.
Losargumentos demain( ) sehan denominado arge yargv por conve-
nio.Esto quieredecir quepodriamos utilizar otros identificadores paranom-
brarlos.
El siguiente ejemplo muestra como acceder al contenido deestos pa-
nimetros desdeun programa. El programa quesepresenta acontinuacion,
simplemente escribe los argumentos pasados desde la linea de ordenes a
lafuncion 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+ +);
Ordende ejecucion:
C: \ >prog01 uno dos tres
argumento 1: uno
argumento 2: dos
argumento 3: tres
Por ser argv un array depunteros acadenas, existenvarias formasde
acceder al contenido de sus elementos. Estas son:
EI numero de parametros deuna funcion puede ser variable. La notaci'
empleada. para especificar esta caracteristica es laelipsis; tres puntos (...
Por ejemplo:
Este ejemplo declara una funcion queno retorna un valor, yquep
de tomar un numero variable de argumentos de tipo no especificado,
Si algunos argumentos, pero no todos, son conocidos, estos deben
listados primero. En estecaso, el numero deargumentos especificados
laHamada, debe ser como minima igual al numero deparametros 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
lapila (stack). El recuperar de la pila estos argumentos es responsabilidad
del programador. Para ello, y con el fin de tener un punta de referencia,
sedebe 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 compi lador 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:
Seutiliza para declarar la variable ap (puntero a un char), que utiliza-
remospara 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 (subindice -1).
Inicializa la variable ap al valor NULL. Cuando necesitamos recupe-
rar mas de una vez los argumentos, es necesario Hamar previamente aesta
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
lamacro va-start( ). Esta macro toma como argumentos lavariable p y
el ultimo argumento formal declarado enla funcion. Lamacro va_arg( )
esutilizada para coger los argumentos no nombrados, enorden. En lalla-
mada, el programador debeespecificar el tipo. Antes deretornar desdeuna
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.
Sediceque una fundon esrecursiva, si sellama asl misma. El compilador
C permite cualquier numero de llamadas recursivas a una funcion. Cada
vezquelafundon esllamada, los panimetros formales ylasvariables auto
yregister soninicializadas. Notar quelasvariables static solamente sonini-
cializadas una vez, en la primera Hamada.
La funci6n factorial, cuyo programa sepresenta 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 seveel 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
derecursi6n. Cuando seHegaan = 0, seobtiene como resultado el valor
1yseinicia lavuelta hacia el punta departida, reduciendo el nivel dere-
cursi6n en una unidad cada vez.
Los algoritmos recursivos son particularmente apropiados cuando el
problema aresolver 010sdatos atratar sedefinen en forma recursiva. Sin
embargo, el uso de la recursi6n debe evitarse cuando haya una soluci6n
obviapor iteraci6n.
En aplicaciones pnicticas es imperativo demostrar que el nivel maxi-
mo derecursi6n es, no s610finite, sino realmente pequeno. La raz6n es
que, por cada ejecuci6n recursiva delafunci6n, senecesita cierta cantidad
dememoria para almacenar las variables locales y el estado en cursa del
proceso de calculo con el fin de recuperar dichos datos cuando seacabe
una ejecuci6n y haya que reanudar la anterior.
Como hemos indicado anteriormente, elHamar a una funci6n recursiva-
mente, consume mucho espacio depila debido aquepar cada Hamada las
variablesqueintervienen enla funci6n son salvadas enlapila para poste-
riormentepoder iniciar lavuelta. Esto indica quepuede 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. Sepide
realizarunprograma quefusione ambas listas, enotra 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)
[
f or (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)
return(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 quecuente el numero devecesqueaparece cada
unadelasletras (a- z) enuntexto 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]) <= C
z
')
conta[text[iJ - ca']++;
El calendario Gregoriano actual obedece alareforma del calendario julia-
no queorden6 el papa Gregorio XIII en 1582. Sedecidi6, despues dealgu-
nasmodificaciones, queen10 sucesivo fuesen bisiestos todos los alios mul-
tiplosdecuatro, pero quedelos alios seculares (Iosacabados endos ceros)
s6lo fuesen bisiestos aquellos que fuesen multiplos de cuatrocientos. En
basea estos conceptos, construir un programa para que dada una fecha
(dia, mes y alio), nos devuelva como resultado el correspondiente dia de
lasemana.
Ladescomposici6n ensubproblemas quesehahecho enlarealizaci6n
deesteejercicio 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)
[
case2:
if (r =AnnoBisiesto(anno))
diaB = (dia >= 1) && (dia <= 29);
else
diaB = (dia >= 1) &&(dia <= 28);
break;
case4: 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 teclapara 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) I I (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 10sarrays, el nombre deuna funcion representa la
direccion decomienzo delafuncion; por 10tanto, puede ser utilizado para
pasarl0 a funciones, colocarlo en arrays, etc.
p-.-identif identifica auna variable tipo puntero. Esta variable recibini un
puntero auna funci6n, dado por el propio nombre delafunci6n.
En el siguiente ejemplo, sedefine un puntero pauna funci6n. A con-
tinuaci6n, seasigna ap la direcci6n dela funci6n escribir y sellama 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 siguienteejemplo clarifica estacaracteristica deC. EI programa que
semuestra a continuaci6n, busca y escribe el valor menor deuna lista de
datosnumerica 0 deuna lista dedatos alfanumerica. Si enla linea de6r-
denessepasa un argumento "n", seinterpreta lalista como numerica; en
10sdemas casos seinterpreta como alfanumerica.
EI programa consta fundamentalmente deuna funci6n fmenor( ) que
buscaenuna listadedatos, el menor, independientemente delasoperacio-
nesdecomparaci6n (numerica0alfanumerica). Para ello Iepasamos fun-
ciones diferentes para comparar. Para hacerlo numericamente, seutilizala
funcion compnu( ) y para comparar alfanumericamente, seutiliza lafun-
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 ycompa/ son punteros alas funcio-
nesdel 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 estecapitulo hemos estudiado como el usuario puede definir sus pro-
pias funciones. No obstante C dispone ensus librerias demas de400fun-
ciones; algunas deellasyalas hemos visto, como las funciones para entra-
da/salida, las funciones para manipular cadenas decaracteres etc., yotras
las iremos viendo en este y en sucesivos capitulos.
Las declaraciones para las funciones matematicas que a continuacion se
describen, estan enel fichero aincluir math.h. Quiereesto decir, quecuan-
do seuti liceuna funcion matemcitica en un programa, debe especificarse
la directriz:
Losargumentos para estas funciones son detipo double yel resultado
devuelto es tambien de tipo double. Por ello, en muchos casos utilizare-
moslaconstrucci6n cast para convertir explicitamente los argumentos al
tipo deseado.
Laexpresi6n (double) haceque valor seaconvertido atipo double an-
tesde ser utilizado.
Podemos clasificar las funciones matemcHicasen las siguientes cate-
gorias:
Estafunci6n da como resultado el arco, en el rango 0 a 1f, cuyo coseno
esx. EI valor de x debe estar entre -1y 1; de 10 contrario se obtiene un
error DOMAIN (argumento fuera del dominio de la funci6n).
Estafunci6n da como resultado el arco, enel rango -1f12 a1f12, cuyo seno
esx. EI valor dexdebeestar entre-I y 1, si no seobtiene un error DOMAIN
(argumento fuera del dominie de la funci6n).
Esta fund6n da eomo resultado el areo, en el rango -7r12 a ni2, euyatan-
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, seobtiene 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);
Estafund6n dacomo resultado el coseno hiperb6lico dex (xenradianes).
double cosh(double x);
Estafunci6n da como resultado el seno hiperb6lico de x (x en radianes).
double sinh(double x);
Estafunci6n dacomo resultado latangente hiperb6lica dex(xenradianes).
double tanh(double x);
Estafund6n da como resultado el valor de eX (e
double exp(double x);
Estafund6n 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 0igual 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 endoble 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 0igual que x.
double x =2.8, y =-2.8;
printj(<t%g %g \ n': jloor(x), jloor(y));
Esta funcion da como resultado x
Y
Si x es 0 ey negativo; 0 si x ey son
0; 0si xes negativo ey no esentero, seobtiene un error DOMAIN (argu-
mento fuera del dominio dela funcion). Si x
Y
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,
ocurreun error DOMAIN (argumento fuera del dominio de la funcion).
Lasfunciones delalibreria matematica Hamanalafuncion matherr cuan-
do ocurre un error.
struct exception
[
int type;
char *name;
double argl, arg2;
double retval;
} .x;
tipo de error
funcion donde seorigina el error
valores que causan el error
valor devuelto
DOMAIN
OVERFWW
EI argumento estafueradel dominie delafunci6n.
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 esdemasiado pequeno para ser repre-'
sentado.
PWSS
SING
TWSS
UNDERFWW
En el ejemplo siguiente semuestra laforma deutilizar lafunci6n mat-
herr(), lacual seraHamada automaticamente si "algunafunci6n 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;
]
]
Estafundon da como resultado un numero pseudoaleatorio entero, entre
o y 32767.
Estafuncion fija el punta decomienzo para generar numeros pseudoalea-
torios. Si no seutiliza, e1punta decomienzo siempreesel 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, enel momento que lafundon clock( ) esejecutada. Estetiempo
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 lamacro CLOCK---.PER_SEC(version ANSI), ambasde
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 dacomo resultado el numero desegundos transcurridos des-
de las 0 horas dell de Enero de 1970.
Estafuncion convierteuntiempo almacenado como unvalor detipo 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));
f or (x 1; x <= 5; x+ +)
[
do / * tiempo de espera igual a 1segundo */
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
Estafunci6n convierte el numero de segundos transcurridos desde 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.
Lafuncion loealtime devuelveun puntero a.laestructura quecontiene
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; 1de 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 deladivision deDU
merador entre denominador.
Lafunci6n div() devuelveel resultado (cociente y resto) enlaestruc-
tura div_t,. definida en el fichero stdlib.h.
typedef struct _div_t
[
int quot;
int rem;
] div_t;
/ * cociente */
/* resto */
Lafunci6n ldiv() realiza lamisma funci6n que div(), pero para valo-
resdetipo long. EI resultado esdevueIto enlaestructura Idiv_t, definida
enel fichero stdlib.h.
typedef struct _ldiv_t
[
long quot; / * cociente */
long rem; / * resto */
]ldiv_t;
Estafunci6n ordena un array, utilizando el algoritmo quick-sort. La fun-
cionno retorna -unvalor.
comparar es un puntero auna 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);
elseij (~rgl >*arg2)
return (asc_des);
else
return (0);
Esteprograma leeuna listadenumeros ylaclasifica, utilizando lafun-
cion qsort( ), en orden ascendente 0 descendente.
Lafuncion qsort( ), implementada enC, recibe como panimetros: la
direcciondecomienzo del array, el numero deelementos aordenar, lalon-
gitudenbytes decada elemento yel nombre deuna funcion implementada
por el usuario para comparar dos elementos. El nombre de una funcion
esunaconstante devalor, ladireccion decomienzo delafuncion. Esta fun-
cionesHamada por qsort( ) y en laHamada Iepasa las direcciones de los
doselementosacomparar, segunseveenladefinicion; deahi que, ennuestro
caso, losargumentos delafuncion comparar( ) seandos punteros aenteros.
Estafuncion 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.
esun puntero auna funci6n definida por el usuario, quecom-
para dos elementos y retorna un valor:
Esta funci6n retorna un puntero alaprimera ocurrencia deobjeto en
el array apuntado por base. Si la busqueda falla, la funci6n retorna un
NULL. (Ver tambien la fund6n qsort( )."
I find(o bjeto , base, num, bytes, co mparar)
I search(o bjeto , base, num, bytes, co mparar)
Las funciones ljind( ) y lsearch( ), ejecutan una busqueda lineal deobjeto
enun array no necesariamente clasificado. Si objeto no seencuentra sobre
el array, lafunci6n lsearch() 10 afiadeal final deeste, mientras queljind( ) 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 auna 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 basa en un algoritmo que realiza
comparaciones eintercambios hasta que 10selementos esten ordenados. Este
algoritmo es independiente de las operaciones de comparaci6n eintercam
bio, por 10que pasandole diferentes funciones para comparar, y si espreci
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( ),
Iepasa 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, Iepasa un puntero a la fun-
ci6n strcmp( ) de C, para realizar una comparaci6n alfanumerica de dos
elementos. En ambos casos, cuando la funci6n maio( ) llama a la funci6n
c1asificar( ), Iepasa 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,i ot 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
Lasfunciones deentrada y salida (E/S) delaslibrerias estandar deC, per-
mitenleer y escribir datos a, y desde, ficheros y dispositivos. Un fichero
esunacolecci6n deinformaci6n quealmacenamos enun soporte magneti-
co,generalmente un disco, para poder manipularla encualquier momento.
C tiene disponibles los tres tipos siguientes de funciones de E/S:
En estecapitulo sepresentan las funciones estandar de E/S; sucarac-
teristicafundamental esquelaE/S, enel procesamiento deficheros, serea-
lizaatraves deun buffer 0memoria intermedia. Tambien, permiten laE/S
conformato. C se refiere a estas funciones como "Stream I/O".
Lautilizaci6n deun buffer 0memoria intermedia pararealizar lasope-
racionesdeE/S, esuna tecnica, implementada ensoftware, disefiada para
hacerlasoperaciones deE/S mas eficientes. Un buffer esun area dedatos
en la memoria (RAM)>-asignada-por el programa-qll.e bre el fichera. La
utilizacion debuffers en operaciones deE/S, reduce e1numero deaccesos
al dispositivo fisico (disco por ejemplo) asociado con el fichero, necesarios
para latransferencia deinformacion entre e1programa y el fichero; unac-
ceso a un dispositivo fisico consume mucho mas tiempo que un accesoa
lamemoria (RAM). Cuando un fichero no tiene asociado un buffer, cada
byteescrito a, 0leido desde, el fichero esfisicamente transferido enel mo-
mento de la operacion. En cambio, cuando un fichero tiene asociada un
buffer, todas lasoperaciones deE/S requeridas son servidas desdeesebuf-
fer; latransferencia fisicadedatos sehaceenmultiplos del tamafio del buffer.
Las funciones eShindar de E/S, como su nombre indica, proporcionan la
forma mas normal deE/S en un programa C. Permiten escribir y leer da-
tos de un fichero, de las siguientes formas:
Primera, los datos pueden ser escritos 0leidos caracter acaracter can
las funciones jputc( ) yjgetc( ).
~- Segunda, los datos pueden ser escritos y leidos palabra apalabra can
las funciones putw() ygetw(). Seentiende por palabra, palabra maquina
o valor de tipo into
3- Tercera, los datos pueden ser escritos yleidos como cadenas decarac
teres con las funciones jputs( ) yjgets( ).
,:\. Cuarta, los datos pueden ser escritos yleidos con formato, conlasfun
ciones jprintf( ) yjscanf( ).
Quinta, los datos pueden ser escritos y leidos como registros a blo-
ques, (esto es, como un conjunto dedatos delongitud fija, tales comoes-
tructuras 0elementos de un array), con las funciones jwrite( ) yjread( t
Para poder escribir 0leer sobre un fichero, primeramente hay que abrirlo
conlas funcionesjopen(), jdopen() 0jreopen(). El fichero puede ser abierto
para leer, para escribir 0para leer y escribir; y puede ser abierto en modo
texto 0en 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-
been 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-
viertea ' \ n'; y cuando encuentra un Ctrl +Z 10interpreta como un EOF.
En modo binario estas conversiones no tienen lugar.
Cuando un programa comienza su ejecuci6n, son abiertos automiti-
camente cinco ficheros, que secorresponden 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)
Deestos cinco, dos de ellos, el dispositivo serie y el dispositivo de im-
presionparalelo, depend en de la configuraci6n de la maquina, por 10tan-
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 fuerae
fichero. Estas streams pueden ser utilizadas en cualquier funcion quereo
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 0de escritura, el puntero de LIE avanza a la siguiente
posicion. Cuando un fichero seabre, 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
uti lieemos la funci6n clearerr( ) 0rewind( ) para desactivarlo explicitamente.
Existen tres organizaciones de ficheros basicas, de cuya combinaci6n sede-
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 0directo
Se habla de acceso secuencial cuando se van accediendo posiciones
sucesivas, esto es tras acceder ala posici6n N, seaccede ala posici6n N +1;
y sehabla de acceso aleatorio 0directo 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, 0aleatorio 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 0no seen-
cuentra, se obtiene un error.
Abrir un fichero para escribir. Si el fichero no existe, secrea;
y si existe, su contenido sedestruye 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 0ab +, etc.), para indicar si el fichero seabre en modo tex to
o en modo binario. La opci6n t, no pertenece allenguaje C estandar; sino
que es una extensi6n de Microsoft. Si t 0b 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 auna 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 lafun
ci6njopen(), debemos definir un puntero de tipo FILE, tipo que estade-
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
dememoria (puntero) de un elemento del array de estructuras _iob[ J ; esta
asignaci6n ocurre, por cada fichero que se abre. El array _iob[ J , tiene
unnumero 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 sidoha
bierto por la funci6n jopen( ). La descripci6n para el argumento acceso,
es la misma que la dada en la funci6n jopen( ).
La funci6n jdopen( ) devuelveun 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 porel
usuario. Ladescripci6n para el argumento acceso, eslamisma queladada
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 sedevuelveun
puntero nulo.
# include <stdio.h >
FILE *pj;
pj =jreopen("datos': "w': stdout);
Este ejemplo, reasigna stdout al fichero Hamado datos. Ahora, 10que
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 deerror para esefichero sepone
activo y permanece enesteestado, hasta que seaejecutada lafunci6n 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 deerror y el indicador defin defiche-
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 seintenta 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 sellegue 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 0un 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 =J open("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 0un EOF si ocurre un
error 0se detecta el final del fichero. No obstante, ya que EOF es un valor
aceptado, utilizar la funci6n ferror( ) 0feof( ) para distinguir si sehade
tectado el final del fichero 0si 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 leeel 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 detipo int, enel fichero apun-
tado por pf.
La funci6n putw( ) devuelve el valor escrito 0un 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 leeel siguiente valor binario detipo int, del fichero apuntado
por pf y avanza el puntero de LIE al siguiente valor no leido.
Lafunci6n getw( ) devuelveel valor leido 0unEOF si ocurre un error
o sedetecta el final del fichero. No obstante, yaque EOF esun valor vali-
do, utilizar lafunci6n jerror( ) 0jeof( ) para distinguir si seha detectado
el final del fichero 0si 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 leeuna cadena de caracteres, cadena, del fichero apuntado
por pf. Laterminaci6n ' \ 0' esafiadida automaticamente alacadena lei-
da. Seentiendepor cadena desdelaposici6n actual dentro del fichero, hasta
el primer canicter nueva linea (' \ n') incluido este, hasta el final del fiche-
ro, 0hasta 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 0que se ha detectado un
EOP. Utilizar feof( ) 0ferror( ) para determinar 10que 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: cpr i nt
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-J ichero. \ 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 0un
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 seespe-
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 cada 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, totaLJ eal = 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 seocupa 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( ) 0jerror( ) para distinguir si se ha detectado el final del fichero 0
si ha ocurrido un error. Si n 0c son 0, jread( ) devuelve un 0y 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 +Zpara 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);
d o
[
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 leeregistros formados por dos campos, rejerencia ypre-
cio, ylos almacena enun fichero llamado datos. Una vezcreado 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 2bytes, un long ocupa 4bytes, un
jloat ocupa 4 bytes y un double ocupa 8bytes. No hay que confundir el
formato binario empleado para almacenar un dato numerico, con el modo
binario enel que seabre un fichero, 10cual s6lo indica que sevaaefectuar
una conversi6n de los finales de linea y del final de fichero.
Con ficheros abiertos enmodo texto, pueden producirse resultadosines-
perados debido alaconversi6n de ' \ n' enCR + LF. Par ella, esaconseja-
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 0escritura. 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 0no 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 0escritura.
El argumento buffer es un array de caracteres de longitud n bytes que
desempefia la funci6n de buffer 0memoria inter media; el argumento tipo
puede ser:
La fundon setvbuf( ) devuelve un 0si 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(" \ nEI fichero %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);
r etur n 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
r etur n 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 deta
mafio fijado por el usuario; y tercero, sin asignar un buffer al fichera. La
prueba mas satisfactoria, es lasegunda; en ella el tamafio del buffer esmayor.
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.
Lafunci6njjlush() devuelveel valor 0si no ocurre un error y unEOF
encaso contrario.
Estafunci6n crea un fichero temporal. Este fichero es automaticamente
borrado cuando el programa termina normalmente, 0cuando seborra ex-
plicitamente con la funci6n rmtmp( ). El fichero temporal es abierto en
modo w+b.
Lafunci6n 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");
Estafunci6n es utilizada para borrar todos los ficheros temporales crea-
dospor 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 0un puntero nulo si el nombre existe 0no 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 comple-
to esta formado por el camino especificado por dir, mas el nombre delfi-
chero compuesto por el prefijo pr ef y la cadena generada.
La fund6n tempnam( ) devuelve un puntera a la cadena de caracteres
generada 0un puntero nulo si el nombre existe 0no puede generarse.
FILE *pj3;
char *nombre3;
/ * Crea un nombre con el prejiJ o "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, mueveel puntero de LIE asociado con el fichero apuntado
por pf, auna nueva10calizaci6ndesplazada desp bytes delaposici6n dada
por el argumento pos.
La funci6n jseek( ) devuelve un 0si no se ha producido un error y
un valor distinto de 0en caso contrario.
Este ejemplo situa el puntero deLIE, al principio del fichero apunta-
do por pjichero. Observar queel desplazamiento es0yestaexpresado 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 10tanto en mod o texto, las operaciones con la funci6n jseek( )
seran buenas con un desplazamiento 0(ir al principio 0al final) 0can 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* nOtotal 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
d o
[
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
d o
[
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
Esteprograma ilustra el uso de las funcionesjseek(), jtell() y rewind( ).
El programa abre un fichero Hamado datos con registros de tipo registro,
calculael 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 0si 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 0si 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
Estafuncion abre e1fichero especificado por path y permite compartirlo
por otros procesos. El argumento acceso especifica como es abierto el fi-
cheroy el argumento shesuna constante queindica losatributos para com-
partir el fichero.
#include <stdio.h >
#include <share.h >
acceso: r, w, a, r+, w+, a+
Para indicar dtipo de conversion, pueden aiiadirse 10scaracteres
to b.
sh: SH_COMPAT, SH~ENYNO, SH~ENYRD,
SH~ENYRW, SH~ENYWR
La funcion -fsopen( ) devuelve un puntero a una estructura detipo
FILE, la cual secorresponde con el fichero abierto. Un puntero nul0indio
ca un error.
Si SHARE.COM (0SHARE.EXE) no esta instal ado, MS-DOS igno
ra el modo compartido.
Lasfunciones de E/S de bajo nivel, denominadas en C "Low-level I/O func-
tions", ofrecen pocas 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, sedan 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).
Lasfunciones .de E/S de bajo nivel son, generalmente, incompatibles con
lasfunciones de E/S estandar. Las funciones de E/S estandar pueden pro-
cesar ficheros que tengan, 0no, asociado un buffer; mientras que las fun-
cionesde E/S debajo nivel s610pueden 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, 0leido 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 0leer sobre un fichero, primeramente hay que abrirlo
con las funciones open ( ), sopen( ) 0creat( ). El fichero puede ser abierto
para leer, para escribir 0para leer y escribir y puede ser abierto en modo
texto 0en modo binario.
Estas funciones devuelven un mimero denominado descriptor defi-
chero, que sera utilizado mas tarde para referirse al fichero en las opera-
cionesdelectura/escritura. El descriptor asignado aunadeterminada stream,
puede saberse por medio de la funci6n jileno( ).
Cuando un programa comienza su ejecuci6n, cinco ficheros, quese
corresponden condispositivos, sonabiertos automaticamente. Estos ficheros,
direccionados por streams, y los dispositivos asociados por defecto son:
Deestos cinco, dos deellos, el dispositivo serieyel dispositivo deim-
presi6n paralelo, dependen delaconfiguraci6n delamaquina, por 10tan-
to pueden no estar presentes.
Despues de haber finalizado el trabajo con un fichero, hay que cerrarlo
con lafunci6n close(). Si un fichero no secierra explicitamente, escerrado
automaticamente cuando finaliza el programa. Sin embargo, esaconseja-
blecerrar un fichero cuando seha finalizado con el, yaque el numero de
ficheros abiertos al mismo tiempo esta limitado.
Lasoperaciones delectura y escritura serealizan por medio delas funcio-
nesread() y write(). Siempre empiezan en laposicion sefialada por el de-
nominado puntero de lectura escritura (LIE).
Laposicion deLIE puede ser situada en cualquier parte del fichero,
utilizando lafuncion lseek(). Para determinar enque posicion nos encon-
tramos tenemos la funcion leU( ).
Estafuncion abre el fichero especificado por path para el tipo de opera-
cionindicado por modo.
#include <fcntl.h >
#include <io.h >
#include <sys \ types.h >
#include <sys \ stat.h >
#include <errno.h >
El argumento modo esuna expresion entera formada por una 0mas
constantes separadas por el operador OR (I). Estas constantes definidas
enfcntl.h son las siguientes:
Abreun fichero para afiadir informacion al final del
mismo.
Crea un nuevo fichero para escribir. Si el fichero exis-
te, esta constante no tiene efecto.
Devuelve un numero de error (variable errno) si el
fichero especificado existe. Solamente se utiliza con
O_CREAT.
Abre un fichero s610para 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 0O_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 esposible asignar bajo DOS aun fichero, el atributo des610escri-
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.00posteriores y con la orden SHARE
instalada, hay problemas si seabre un fichero en el modo O_CREAT I
O-l{DONLY 0en el modo O_CREAT I 0_WRONLY y con un valor
S_IREAD parapmodo. Lasoluci6n al problema que surge(secierra pre-
maturamente el fichero), es abrir el fichero en el modo O_CREAT I
O-l{DWR.
Lafunci6n open ( ) retorna un numero (descriptor de fichero) que ha
sidoasociado al fichero abierto, para dirigirse ael enposteriores operacio-
nesdelectura 0escritura. Un valor -1 indica un error y la variable errno
espuesta a uno de los siguientes valores:
EACCES
EEXIST
EMFILE
ENOENT
EINVAL
Permiso denegado
Fichero existe. O_CREAT yO~XCL sonespecificados
Demasiados ficheros abiertos
No existe tal fichero 0directorio
Argumento invalido
El numero devuelto por la fund6n open( ), secorresponde coneli
dicedel elemento del array deestructuras _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 fichero
H
);
else
printftjichero abierto para leer \ n");
nf2 =open("datos2': OJ DWR I O_CREAT, S~READ I S~WRITEt
if (nf2 ==-1)
perror(Herror al abrir el fichero
H
);
else
printftjichero abierto para leer y escribir \ n
H
);
Este programa utiliza lafund6n 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, IeSO D
asignados lospermisos especificados (lecturayescritura). Si el ficheroexist~
los permisos son ignorados.
Esta fund6n abre un fichero especificado por path para escribir. Si elfi
chero existey tiene permiso de escritura, destruye su contenido. El argu
mento pmodo, seaplica aficheros denuevacreaci6n solamente. Susignifi-
cado es el mismo que seha expuesto en la funci6n open( ).
#include <sys \ types.h >
#include <sy \ stat.h >
# include <io.h >
#include <errno.h >
Lafunci6n creat( ) retorna un numero queha side asociado al fichero
creado, para dirigirse ael enposteriores operaciones delectura 0escritura.
Unvalor -1 indica un error y lavariable errno espuesta 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");
Estafunci6n indica si seha llegado al final del fichero cuyo numero aso-
ciadoes num.
#include < io.h >
#include <errno.h >
Lafund6n eo/( ) devuelveel valor 1si seha llegado al final del fiche-
ro;encaso contrario, devuelveel 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 esteejemplo, seIeendatos del fichero referenciado por nf EI pro-
ceso finaliza cuando sea1canzael final del fichero 0si 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 10que permitini que sea utilizado por otro fichero.
# include <io.h >
# include <errno.h >
La funci6n closer ) devuelve un 0si el fichero es cerrado. Un valor
-1 indica un error y la variable errno es puesta al valor correspondiente.
Estafunci6n escribecbytes, almacenados enbuffer, enel 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 ylavariable errno espuesta al valor correspon-
diente.
Estafunci6n 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 debytes actual mente leidos. Un
valor de0indica quesehaencontrado un fin defichero. Un valor -1indica
un error y la variable errno es puesta al valor correspondiente.
EI siguiente programa, recibe como panimetros el nombre dedosfi-
cheros ycopia el contenido del primero sobreel segundo. Si el ficherodes-
tino existe, senos preguntani si queremos sobreescribirlo. Cuando el pro-
grama finaliza, presenta un mensaje para saber si la copia se ha hecho
satisfactoriamente, 0si 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, OJ INARY I O_WRONLY I O_CREAT I
O_TRUNC, S-.J READ I S-.J WRITE);
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-J uente);
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.
Estafuncion dacomo resultado laposicion actual del puntero deLIE, del
ficheroasociado con num. Estaposicion esrelativaal 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
variableerrno es puesta al valor correspondiente.
EI siguiente programa, solicita de la entrada estandar, el nombre de
unfichero y un caracter. EI resultado que sebusca es la posicion del pri-
mer caracter, existente dentro del fichero, igual al dado y e1numero total
debytes 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 ;
d o
(
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
}
Estafunci6n asocia un segundo descriptor, al fichero asociado con e1des-
criptor num.
#include <io.h >
#include <errno.h >
Lafunci6n dup( ) devuelveun nuevo descriptor si seejecuta satisfac-
toriamente. Un valor -1 indica un error y lavariable errno espuesta al va-
lor correspondiente.
Estafunci6n asocia un segundo descriptor num2, al fichero asociado con
el descriptor numl. Si yaexisteun fichero abierto con el descriptor num2,
estees cerrado.
#include <io.h >
#include < errno.h >
Lafunci6n dup2() devuelveun 0si seejecuta satisfactoriamente. Un
valor-1 indicaun error ylavariableerrno espuestaal valor correspondiente.
Estafunci6n abre el fichero especificado por path para el tipo de opera-
cionindicado por modo, ypermite queseacompartido 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 OMPAT, SH_DENYNO, SH_DENYRD,
SH~ENYRW, SH_DENYWR
(Ver -J sopen( ))
S_IWRITE, S_IREAD, S_IREAD I S_IWRITE
(Ver open( ))
Lafundon sopen() retorna un numero (descriptor defichero) queha
sido asociado al fichero abierto, para dirigirseael enposteriores operacio-
nes de lectura 0escritura. Un valor -1 indica un error y la variable errno
es puesta al valor correspondiente.
Si SHARE.COM (0SHARE.EXE) no esta instalado, MS-DOS igno-
ra el modo compartido.
Esta funcion pone lamascara depermisos del proceso actual, al modo es-
pecificado por pmodo. Esta mascara es utilizada para modificar los per-
misos asignados a los nuevos ficheros que secreen 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 aO. 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 1en la mascara, cual-
quier nuevo fichero creado seria de s610lectura.
Este ejemplo pone el permiso de s610lectura, a todos los futuros fi-
cheros que secreen por medio de las funciones creat( ), open ( ) y sopen( ).
FUNCIONES PARA LA CONSOLA Y
LOS PUERTOS DE E/S
Lasfunciones para laconsola y los puertos de E/S, ejecutan las operacio-
nesdelecturayescritura sobrelaconsola(teclado/pantalla) 0sobreunpuer-
toespecifico. Lasalida y laentrada deestas funciones pueden ser redirec-
cionadas. La redirecci6n se efectua a nivel de sistema operativo.
Estasfunciones son especificas deMS-DOS yOS/2. Las funciones utiliza-
dasparalaentrada dedatos, leenlosmismos delaentrada estandar (stdin)
y lasfunciones utilizadas para lasalida dedatos, escriben los mismos so-
brelasalida estandar (stdout). Las declaraciones para todas estas funcio-
nes, estan contenidas en el fichero conio.h.
Leeun caracter del teclado sin que seproduzca eco (el caracter no sevi-
sualizaenpantalla). 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 ala funcion para recuperar el segundo codigo (ver Codigos
Extendidos en los apendices).
Escribe directamente en la consola, el caracter c. No hay conversion deca
racteres: ' \ n' no es convertido a CR + LF.
La funcion putch( ) retorna el caracter c si seejecuta satisfactoriamente
y un valor EOF si ocurre un error.
Estafuncion chequea el teclado, buscando el canicter correspondiente a
latecla mas recientemente pulsada.
La funcion kbhit( ) devuelve un valor distinto de 0si seha puisado
unatecla, si no devuelve un valor O.
printf(HPulse una tecla para continuar ");
while (!kbhit( ));
Estafuncion devuelve a la memoria intermedia de la consola, el ultimo
canicter leido, haciendo que este caracter sea el proximo caracter a leer.
Lafuncion ungetch( ) devuelveel caracter car si existe. Si el valor de-
vueltoesEOp, 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 leecaracteres hasta leer un digito. EI digito leido esde
vuelto al buffer por lafunci6n ungetch( ), para que acontinuaci6n lafun
ci6n cscanf( ) lea el valor tecleado.
Leedirectamente de laconsola una cadena decaracteres, hasta encontrar
el canicter ' \ n', yalmacena enbuffer lacadena ysulongitud, delaforma
siguiente:
buffer[O] contiene el maximo numero decaracteres quepueden leerse,in
cluyendo el canicter nulo de terminaci6n.
buffer[l] contiene el numero decaracteres leidos, excluyendo el canicter
nulo de terminaci6n.
La funci6n cgets( ) retorna un puntero al comienzo de la cadena de
caracteres (direcci6n de bujjer[2]).
lafuncion cputs( ) retorna un 0si seejecuta satisfactoriamente y un valor
distinto de 0si ocurre un error.
Leedatos directamente delaconsola, los interpreta deacuerdo con el for-
matoindicado ylosalmacena enlosargumentos especificados. No haycon-
versionde caracteres: CR+ LF no es convertido a ' \ n'. Cuando se leen
variosdatos consecutivos, no hay que poner especial cuidado en eliminar
el canicter LF, que normalmente queda en el buffer del teclado.
Lafuncion cscanf( ) retorna un entero, correspondiente al numero de
datosleidos y asignados, 0un EOP cuando seintenta leer un end-of-file.
Escribedirectamente en laconsola datos con formato. No hay conversion
decaracteres: '\ 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 lautilizacion del canicter ' \ r' debido aqueno hay conver
sion (' \ n' aCR +LF). Observar tambien, que despues decscanf( ) noes
necesario limpiar el buffer del teclado; al no quedar enel mismo el canic
ter LF, gets( ) se ha ejecutado normalmente.
Con respecto al teclado, sabemos queal pulsar una tecla segenera un byte,
correspondiente al codigo ASCII del canicter (ver codigo decaracteresde
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.
Sinembargo, existen muchas teclas y combinaciones deteclas no re-
presentadas por un unico byte. Por ejemplo las teclas FI aFl6 0lacombi-
naci6ndeteclas AU+A (ver c6digos extendidos enlos apendices). El c6di-
godeestas consta de dos bytes. El primer byte siempre vale O.
char byte], byte2;
byte] =getch( ); byte2 =getch( );
printf("%d O/Od': byte], byte2);
Esteejemplo da como resultado el c6digo extendido correspondiente
aestetipo deteclas. Por ejemplo si pulsamos FI, seescribe 059, si puIsa-
masAU+A seescribe 030. Observar que tenemos que emplear dos veces
lafunci6n getch( ).
El sistema operativo MS-DOS dispone de un sistema para controlar
directamenteel cursor sobre lapantalla. Este sistema no esta implementa-
dosabrelamemoria ROM como las rutinas normales deacceso adisposi-
tivosde E/S, sino que esta contenirn un fichero separado llamado
ANSI.SYS.
Parapoder utilizar este fichero, debe ser previamente instalado en el
sistemaoperativo. Para ello incluir enel fichero del sistema CONFIG.SYS
laorden: DEVICE = ANSI.SYS.
ANSI.SYS, para controlar el cursor, interpreta secuencias de escape
seguidasde uno 0mas caracteres especiales.
Los caracteres especiales que podemos poner acontinuaci6n, sonlas
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, esalmacenado enmemo-
ria con dos bytes. Un bytecontiene el c6digo normal del caracter yel 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 sepueden elegir de
entrelos presentados en la tabla siguiente:
Negro
Rojo
Verde
Amarillo
Azul
Magenta
Cyan
Blanco
Estas secuencias decaracteres pueden ser transmitidas por lafunci6n
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 deestemenu seeligesituandonos sobreellacon las
teclasdemovimiento del cursor (arriba, abajo). Laopci6n elegidasiempre
estaen video inverso. Para ejecutarla sepulsa 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----.D80
# 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. Lospuer
tos se identifican por direcciones especiales de E/S de 16bits. 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 aun 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 0MCGA 0VGA 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 0a65535 (OxOOOO
a OxFFFF).
Esta funcion escribe un byte en el puerto de salida especificado. EI valor
depuerto puede ser cualquier entero sin signo, en el rango 0a 65535; byte
puede ser cualquier entero, en el rango de 0a 255.
Esta funci6n leeuna palabra dedos bytes del puerto deentrada especifica
do. EI valor del argumento puede ser cualquier entero sinsigna, enel ran
go 0a 65535.
Esta funci6n escribeuna palabra dedos bytes enel puerto desalidaesp
ficado. EI valor depuer to puede ser cualquier entero sinsigno, enel rang
o a 65535; palabra puede ser cualquier entero, en el rango de 0a65535
EI siguiente programa emite por el altavoz un pitido deuna deter
nada frecuencia y duraci6n.
EI metoda consiste en utilizar el temporizador programable inte
del PC (timer) para enviar al altavoz una frecuencia determinada. El ti
(8254) obtiene una sefial proporcionada por el reloj principal del PC,q
oscilaauna frecuencia de 1193180Hz. Para producir un sonido conelte
porizador primero, hay que programar el temporizador para generar
frecuencia; a continuaci6n, sedebe de dirigir la salida del temporiza
al altavoz. El sonido seproducini indefinidamente, amenos que sedesac-
tiveel altavoz.
EI temporizador seprograma proporciomindole un numero. Cuando
recibeuna orden de dirigir su salida al altavoz, cuenta pulsos dereloj del
sistema(oscila a 1193180Hz) hasta alcanzar el valor proporcionado. En:-
toncesseproduce un pulse y comienza la cuenta otra vez desde cero. De
acuerdo con esto, para un sonido de frecuencia de valor 987Hz, progra-
maremos e1temporizador con un valor: c =1193180/987.
1* Emitir un sonido por el altavoz. Utilizar los puertos
* 66, 67y 97para 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 deMicrosoft C soporta todas las directrices definidas en
ANSI C, lascuales permiten sustitucion demacros, compilacion condicio-
nal einclusion de ficheros fuente; y ademas aporta otras para el control
delanumeracion delineas en diagnosticos y enladepuracion, 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
deblanco, en una linea, eindica al preprocesador una accion especifica
aejecutar. Pueden aparecer encualquier parte del fichero fuente, pero so-
lamente seaplican desde su punta dedefinicion hasta el final del progra-
ma fuente.
Ladeclaracion deuna directriz puede ser continuada enuna linea si-
guiente, colocando el caracter \ inmediatamente antes del caracter nueva
linea (NL).
Cuando seejecuta 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, seexpanden las macros, las secuencias
de escape son reemplazadas por sus equivalentes y las cadenas,de
caracteres adyacentes son enlazadas.
~
DIRECTRIZ # d efi ne. Susti tuci 6n d e sr mbolos
Esta directriz es utilizada para asociar identificadores con palabras clave,
constantes, sentencias y expresiones. Cuando un identificador representa
sentencias 0expresiones se denomina macro.
La directriz #define sustituye todas las apariciones identificador 0iden
tificador(panimetros) en el fichero fuente por tex to.
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 tex to.
# define ANCHO 70
# define LONGITUD (ANCHO + 10)
Este ejemplo define un identificador ANCHO como la constante 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 deLONGITUD 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 esutilizado solamente con macros que reciben argumentos.
Este operador precediendo al nombre de un panimetro formal enlama-
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");
Esteoperador al igual queel anterior, tambien esutilizado con macros que
recibenargumentos. Esteoperador permitelaconcatenaci6n dedos cadenas.
printf("elementa " "1" " = %d \ n': elemental);
printj("elementa 1= %d \ n': elemental);
Cuando seutiliza esteformato, sesustituye estalineapor el contenido
del fichero especificado. El fichero sebusca en primer lugar en el directo-
rioactual detrabajo yposteriormente enlos directorios estandar definidos.
Si se utiliza este otro formato, el fichero solamente es buscado en los
directorios estandar definidos.
Las siguientes directrices permiten compilar 0no 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 seejecutara el grupo-de-ltneas aconti-
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, 10cual hace que a y d no puedan ser definidas como varia-
bles de esa c1ase, mientras que silo son b y c. Cuando sedefine ~68000,
las cuatro variables a, b, c y d son declaradas de c1aseregister. 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 estadefinido e # ifndej comprueba
si el identificador no esta definido.
Lalinea # ifdej id esequivalente a # if dejined(id), y lalinea # ifndej
id esequivalente 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).
Estadirectrizvaseguida deun numero entero yopcionalmente deun 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, 10cual hace que el compilador cambie su
contadorinterne ysunombre defichero detrabajo, por los valores especi-
ficadosenestas constantes. Si seomite el nombre de fichero, seutiliza el
quetengala constante ----.FILE __ por defecto.
Lainformaci6n proporcionada por hi directriz # line seutiliza sim-
plementecon el objeto dedar mensajes deerror mas informativos. Cuan-
doseejecuta esta directriz, lasiguiente linea detexto atomar eslaindica-
da por la constante entera especificada y del fichero especificado. EI
compilador utiliza estenumero delinea yfichero deinformaci6n para dar
losavisos 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 unpro-
ceso de compilaci6n condicional. Si sedetecta 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 dela
opci6n especificada, 0por defecto, utilizando la directriz #pragma. A con-
tinuaci6n exponemos algunas de ellas.
Instruye al compilador para activar (on) 0desactivar (off) el chequeo de
punteros; esto es, comprobar si hay punteros nulos ypunteros fuera derango.
Requiere la utilizaci6n de la opci6n IZr. Esta opci6n sera utilizada enla
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) 0desactivar (off) el chequeo de
lapila; 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, estan 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
generacomo una funcion en linea (su codigo es expandido en la llamada).
Losprogramas que utilizan funciones intrinsecas son mas rapidos; aunque
pueden ser mas largos debido al codigo adicional generado. Las funciones
deMicrosoft C que podemos tratar como funciones intrinsecas son lassi-
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 enel programa pertenecientesaesteconjunto, senintratadas como
intrinsecas. Cuando deseemos tratar como intrinsecas s610determinadas
funciones, utilizaremos la directriz #pragma intrinsic(funcl[, junc2} ...}.
Ladirectriz #include afiade el contenido de un fichero dado, .h, en otro
fichero. Los ficheros aincluir pueden ser utilizados para incorporar defi-
nicionesdeconstantes, macros, declaraciones devariables ex temas 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.
Esteapartado 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) yT~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 seha 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" bl e)(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 sepueden 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.8seg
para : jmod( ) con argumentos double
tiempo : 18.12seg
para : jmod( ) con argumentos long double
tiempo : 18.68seg
para : jmod( ) con argumentos jloat
tiempo : 24.39 seg
para : jmod( ) con argumentos double
tiempo : 21.42seg
para : jmod( ) con argumentos long double
tiempo : 25.32 seg
El preprocesador sustituye cada llamada a una macro por su definicion;
dicho deotra forma, expande lamacro. Las funciones no son expandidas;
cuando sellama auna funci6n seutiliza lapila (stack) para almacenarla
direcci6n de retorno y 10sargumentos 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
Lapropiedad caracteristica delas estructuras dimimicas eslafacultad que
tienen para variar su tamafio y hay muchos problemas que requieren de
estetipo deestructuras. Esta propiedad las distingue claramente delas es-
tructuras estaticas fundamentales (arrays y estructuras). Por tanto, no es
posibleasignar una cantidad fija dememoria para una estructura dimimi-
ca, y como consecuencia un compilador no puede asociar direcciones ex-
plicitascon las componentes detales estructuras. Latecnica que seutiliza
mas frecuentemente para resolver este problema consiste en realizar una
asignacion dinamica dememoria; esdecir, asignaci6n dememoria para las
componentes individuales, al tiempo queson creadas durante laejecuci6n
del programa, en vez de hacer la asignaci6n durante la compilaci6n del
mismo.
Cuando setrabaja con estructuras dinamicas, el compilador asigna
unacantidad fija dememoria para mantener ladireccion del componente
asignado dimimicamente, en vez de hacer una asignaci6n para el compo-
nenteen 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 0referencias a otros datos.
Cuando seasigna memoria dimimicamente para un objeto deun tipo cual-
quiera, sedevue1veun puntero alazona dememoria 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 espaciode
memoria 0si t es 0, la funci6n retorna un puntero nulo (NULL).
Puesto que el puntero devuelto es aun objeto deun tipo no especifi-
cado (void), utilizar lanotaci6n 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 */
}
Lafunci6n NuevoElemento( ) asigna memoria para un objeto detipo
elemento ydevuelveun puntero adicho objeto. Si no hay espacio suficien-
tepara crear un objeto del tipo especificado, lafunci6n NuevoElemento( )
devuelveun puntero nulo (00NULL).
En programas que utilizan asignaci6n dimimica dememoria, es muy
importante liberar esteespacio cuando no seutilice, yaque si sepierde la
direcci6n que referencia el espacio asignado, dicho espacio no puede ser
liberado y ademas permanece inaccesible.
Para liberar un bloque dememoria asignado por lafunci6n mal/ocr ),
utilizaremos la funci6n freer ).
Unarray dinamico, segun seha definido en el capitulo de punteros, una
vezcreado no permite alterar su tamafio. Si 10que deseamos es una lista
deelementos uobjetos decualquier tipo, originalmente vacia, que durante
laejecuci6ndel programa vayacreciendoydecreciendoelementoaelemento,
segunlasnecesidades previstas enel programa, entonces tenemos quecons-
truir unalista lineal enlaquecadaelemento apunte 0direccioneel siguiente.
Por estemotivo, una lista lineal sela 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 deun 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);
}
Lasoperaciones quepodemos realizar conlistasincluyenfundamentalmente
las siguientes:
typedef struct datos elemento; / * declaracion del tipo elemento */
struct datos
{
iot dato;
elemento ~iguiente;
};
Primero secreaun elemento ydespues sereasignan los punteros, tal como
se indica a continuaci6n:
q =NuevoElemento( );
q->dato =n;
q- >siguiente =p;
p = q;
/ * asignacion de valores */
/ * reasignacion de punteros */
Esteconcepto nos sugierecomo crear una Iista. Para ello, y partiendo
deuna lista vacfa, no tenemos mas que repetir laoperacion deinsertar 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 delos elementos enlalista, esel inverso del orden
enel que han llegado.
Lainsercion 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;
Lainserci6n deun elemento enlalistaantes deotro elemento apunta-
do por p, sehaceinsertando un nuevo elemento detnis del elemento apun-
tado por p, intercambiando previamente los valores del nuevo elementoy
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 operacionesa
realizar son las siguientes:
q =p- >siguiente;
p- >siguiente =q- >siguiente;
jree(q);
Para borrar un elemento apuntado por p, las operaciones a realizar
sonlas siguientes:
q =Py siguiente;
*P =*q;
jree(q);
Como ejercicio, escribir lasecuencia deoperaciones quenos permitan
barrar el ultimo elemento de una lista.
Recor r i d o d e una Ii sta cuyo pr i mer elemento esta apuntad o
par p
Supongamosque hay que realizar una operaci6n con todos los elementos
deunalista, cuyo primer elemento esta apuntado por p. Por ejemplo, es-
cribirel valor de cada elemento de la lista. La secuencia de operaciones
eslasiguiente:
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 seencuentra el elementa,0
bien, cuando sellega 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 acontinuaci6n un programa quenosper-
mita crear una listaclasificada, enlacual cadaelemento constededoscam
pos: uno que contenga un numero entero y otro que seaun puntero aun
elemento del mismo tipo.
1. Afiadir un elemento. Esta funci6n comprendeni dos casos: inser
tar un elemento al principio delalista0insertar un elementodes
pues de otro.
2. Borrar un elemento delalista. Esta funci6n buscani el elemento
a borrar y despues 10borrani. Hay que distinguir si setratade
la cabecera 0de 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)
{
d o
(
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;
r etur n (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 0quitar
platos se hace siempre por la parte superior de la pila. Este tipo de listas
inserciones
y
supresiones
reciben tambien el nombre delistas LIFO (last in first out - ultimo enen-
trar, primero en salir).
Las operaciones deinsertar 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, 16elimina
de la misma.
Como ejemplo deutilizaci6n deuna pila, vamos asimular una calcu-
ladora capaz derealizar las operaciones de +, -, * y /. La mayoria delas
calculadoras aceptan la notaci6n infija yunas pocas lanotaci6n postfija.
En estas ultimas, para sumar 10y20introduciriamos primero 10, despues
20y por ultimo el +. Cuando seintroducen los operandos, secolocanen
una pila y cuando seintroduce e1operador, sesacan dos operandos dela
pila. Laventaja delanotaci6n postfija esque expresiones complejas pue-
den evaluarse facilmente sin mucho c6digo.
Lacalculadora denuestro ejemplo utiliza lanotaci6n postfija, par 10
quehemos desarrollado dos funciones: push ypop. Lafunci6n pushintro-
duce un valor en la pila y la funci6n pop saca un valor de la pila.
2. Analiza op; si setrata deun operando 10mete enlapila utilizan-
do lafunci6n push( ); ysi setrata deun operador saca, utilizando
la funci6n pop( ), los dos ultimos operandos delapila, 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");
d o
[
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: + - * /
Losdatos 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) sehacen
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 dein-
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, 10elimina de la misma.
Considerese el problema de almacenar las citas diarias, con el finde
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
lacola y la funci6n realizar( ) saca un suceso de la cola.
1. Presenta un menu con lasopciones de: Introducir un suceso, Rea-
lizar un suceso y Salir. A continuaci6n nos solicita que elijamos
una opci6n, 10que da lugar a que seHarnea la funci6n corres-
pondiente.
2. La funci6n introducir( ) comprendeni dos casos: afiadir un ele-
mento auna cola vacia 0afiadir un elemento al final de la cola.
3. La funci6n realizar( ) devuelvecomo resultado el suceso recupe-
rado del principio delacola. Si lacolaestuviesevacia, seindicani
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 esposible acceder acualquier elemento delalis-
ta desdecualquier punta dado. Las operaciones sobreuna listacircularre-
sultan mas sencillas, ya que se evitan casos especiales.
Cuando recorremos una lista circular, diremos que hemos llegadoal
final delamisma, cuando nos encontremos denuevo enel punta departi-
da; suponiendo, desde luego, que el punta departida seguarda dealguna
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 deparada. Esteele-
mento especial recibe el nombre de elemento decabecera delaUsta. 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
deecuaciones algebraicas 0polin6micas de las variables x, y, z. Por ejemplo:
Cada polinomio sera representado como una lista en la que cada ele-
mento representa un termino no nulo, como se indica a continuaci6n:
Aqui COEFICIENTE es el coeficiente del termino x
A
yB zC. Suponemos
que los coeficientes y exponentes estan dentro de los rangos permitidos.
Lanotaci6n 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-
maen orden decreciente del campo ABC, siguiendo la direcci6n de los en-
laces. Por ejemplo, el polinomio 2x
3
y + 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 secorresponde 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 Iistacir-
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 queesta-
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 e r r o r ( v o i d )
{
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 !inomio(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 terminos 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 listadoblemente enlazada, esuna lista lineal enlaquecada 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
serealizan sinninguna dificultad. Sinembargo, casi siempreesmucho mas
facilla manipulaci6n delas mismas, cuando seafiade un elemento deca
becera y existeun doble enlace entre el ultimo elemento y el primero. Esta
estructura recibe el nombre de Iista circular doblemente enlazada.
Como ejemplo, vamos aconstruir una lista doblemente enlazada or-
denada ascendentemente; cada elemento introducido secolocara automa-
ticamente en el lugar que Iecorresponde. 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 einsertar 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)
{
d o
{
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 esuna estructura queidentifica lalista doblemente enlazada
queestamos creando. Contiene dos punteros que definen perfectamente
lalista:prine que apunta al primer elemento, y final que apunta al ultimo
elemento. Para realizar las operaciones deinserci6n y borrado utilizamos
dospunteros auxiliares: paetual queapunta al elemento identificado, ypan-
teriar que apunta ~lemento anterior al identificado.
Unarbol es una estructura no lineal formada por un conjunto de nodos
y unconjunto deraffias. En un arbol existeun nodo especial denominado
raiz.Un nodo del' que salealguna rama, recibe el nombre de nodo de bi-
furcacion 0 nodo rama y un nodo que no tiene ramas recibeel nombre de
Dodo terminal 0nodo hoja.
nivel
0 ralz
nivel 1
nodo de
bifurcacidn
nivel 2
nodo terminal
Arbol
Deun modo mas formal, diremos que un arbol esun conjunto finito
deuno 0mas nodos tales que:
b) Los nodos restantes estan agrupados en D >0conjuntos 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 dera-
mas de un node recibe e1nombre de grado del nodo. El DiveI de un nodo
respecto al node raiz sedefine diciendo que la raiz tiene nivel 0y cualquier
otro node tiene un nivel igual a la distancia de ese node al node raiz. EI
maximo de los niveles se denomina profuDdidad 0altura del arbol.
Es utillimitar los arboles en el sentido de que cadanode sea a10sumo
de grade 2. De esta forma cabe distinguir entre subarbol izquierdo y su-
barbol derecho de un nodo. Los arboles asi formados, sedenominan arbo-
les binarios. --.-/
Un arbol binario es un conjunto finito de nodos que consta de un Dodo
raiz que tiene dos subarboles 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 alaex-
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, eizdo 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 sevisita 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 seaplican 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 0inversa.
Los nombres de preorden, inorden 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 ell as se utiliza
lavariable asignificando la direcci6n de la raiz del arbol con el cual seopera.
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 laclave
deai' ytodas lasdaves del subarbol derecho deaj son mayores que lacia
ve de aj'
Con un arbol deestas caracteristicas encontrar si un nodo deunacia
vedeterminada existe0no, es una operaci6n muy sencilla. Por ejemplo,
observando lafigura siguiente, localizar laclave12esaplicar ladefinicion
de arbol de busqueda, esto es, si la clavebuscada es menor que laclave
del nodo en el que estamos, pasamos al subarbol izquierdo deestenodo,
para continuar la busqueda y si es mayor, pasamos al subarbol derecho.
Esteproceso continua hasta encontrar laclave0hasta llegar aun subarbol
vacfo cuya raiz tiene un valor NULL.
Como ejemplo, consideremos una secuencia declavescon el findede
terminar el numero devecesque aparece cada una deellas. Esto significa
que, empezando con un arbol vacfo, sebusca cadaclaveen el arbol. Sise
encuentra, seincrementa su contador y si no seencuentra, seinsertaen
el arbol como una nuevaclave, con el contador correspondiente inicializa
do a 1.
EI desarrollo completo semuestra acontinuaci6n. EI proceso debus-
queda, funci6n buscar( ), seformula como una funci6n recursiva. Obser
var que al parametro formal raiz delamisma, seIepasa sucorrespondien
teparametro actual por referencia, con el fin dehacer posible los enlaces
entre los nodos.
Una vez construido el arbol, seutiliza lafund6n visualizar_arbol( )
para visualizar e1contenido del mismo. Los nodos sevisitan en inorden
y lasoluci6n sepresenta 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 seestudia el problema de borrar el nodo con clavex, de
un arbol que tiene las claves ordenadas. Este proceso, es una tarea faciI
si el nodo a borrar es un nodo terminal 0si tiene un unico descendiente.
La dificultad sepresenta cuando deseamos borrar un nodo que tienedos
descendientes, yaque con un solo puntero no puede apuntarse en dosdi
recciones. En estecaso, el nodo a borrar debe ser reemplazado, bienpor
el nodo mas a la derecha en el subarbol izquierdo de dicho nodo, 0bien
por el nodo mas a la izquierda en el subarbol derecho.
El proceso detallado, sepresenta acontinuacion ycomprende lostres
casos mencionados:
1. No hay un nodo con claveigual a x.
2. El nodo con clavex tiene un unico descendiente.
3. El nodo con clavex tiene dos descendientes.
La funcion recursiva borrar_D odo() seejecuta solamente enel caso
3. En este caso, se desciende a 10largo de la rama mas a la derechadel
subarbol izquierdo del nodo apuntado por qquesevaaborrar, ysereern-
plaza lainformacion deinteres enel nodo apuntado por qpor losvalores
correspondientes del nodo apuntado por d, que es el nodo mas aladere
chaenel subarbol izquierdo. Lafuncion free(q ) liberalamemoria, del nodo
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 derealizar los enlaces necesarios. LaHa-
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 estaperfectamente equilibrado si, para todo nodo, el ilt!-
mero denodos enel subarbol izquierdo y el numero denodos enel subar-
bol derecho, difieren como mucho en una unidad.
n=1
0
Como ejemplo, considerese el problema deconstruir un arbol perfee-
tamente equilibrado, siendo losvalores delosnodos, nnumeros queseleen
de un fichero de datos, en nuestro caso del fichero predefinido stdin (fi-
chero estandar de entrada).
Esto puederealizarsefacilmentedistribuyendo losnodos, segunseleen,
equitativamente alaizquierda y aladerecha decada nodo. El procesore-
cursivo que seindica a continuaci6n, es la mejor forma de realizar esta
distribuci6n. Para un numero dado n denodos y siendo ni (nodos alaiz-
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 delos siguientes miembros: clave, punte-
ro al subarbol izquierdo y puntero al subarbol derecho.
El proceso detallado sepresenta acontinuaci6n ycomprende una fun-
cionrecursivadenominada construir_arbol(), lacual construye un arbol
den 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 perJ ectamente 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
enlavida 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.
Sedice que un proceso es recursivo si forma parte de si mismo 0sea que
sedefine en funcion de si mismo. La recursion aparece en la vida diaria,
enproblemas matematicos, en estructuras de datos y en muchos otros pro-
blemas.
Larecursion es un proceso extremadamente potente, por 10que la ana-
lizaremos detenidamente para saber cuando y como aplicarla. Esto quiere
decir queaunque un problema par definicion searecursivo, no siempreeste
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
esel numero devecesque sevaallamar asl mismo, esno solo finito, sino
realmente pequeno. La razon esque senecesita cierta cantidad dememo-
ria para almacenar el estado del proceso cada vez que seabandona, tem-
poralmente, debido auna llamada para ejecutar un proceso que esel mis-
mo. El estado en curso del proceso de calculo hay que almacenarlo, para
recuperarlo cuando seacabe lanuevaejecucion del proceso yhayaquerea-
nudar la antigua.
En terminos de lenguaje de programacion, una funcion es recursiva
cuando se llama a sl misma.
Un ejemplo esla funcion deAckerman 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 quenos muestra como solucionar esteproblemaapli-
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) >
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 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<\n\nA(%d,%d) %d\n': 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-
ciondeAckerman, pero sin aplicar la recursi6n. Esto nos exigini salvar las
variables necesarias del proceso en curso, cada vez que la funci6n se Harne
asf misma, con el fin de poder reanudarlo cuando finalice el nuevo proce-
so invocado.
Lamejor 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
nuevaejecuci6n y tomar estos valores de la cima de la pila, cuando esta
nuevaejecuci6n 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) hresultado 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 enel cual esrealmente eficaz aplicar larecursi6n esel pro-
blemadelas Torres de Hanoi. Esteproblema consiste entres barras verti-
calesA, Bye y "n" discos, dediferentes tamafios, apilados inicialmente
sabrelabarra A, enorden detamafio decreciente. El abjetivo esmover los
discas desde la barra A a la barra C, bajo las siguientes reglas:
Una posible soluci6n, esel algoritmo recursivo que semuestra acon-
tinuaci6n:
nOdiscos origen centro destino
inicialmente n A B C
punta 1 n-l A C B
punta2 1 A B C
punta3 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 programa correspondiente a este
problema.
IIIIIIIIIIIII IIIIIIIIIIIIIII main IIIII/ IIIIIIII IIII IIIIIIIIII
main( )
{
printj(HN de discos: ");
scanf(H%d': &n_discos);
movimientos =mover(n_discos, ~: 'B: 'C');
printf(H \ nmovimientos efectuados: %d \ n': movimientos);
IIIIIIIIIIIIIIIIIIIIIIIIIIII mover IIIIIIIIIIIIIIIIIIIIIIIII11/
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 sepropone realizar estemi~moproblema, pero sinuti-
lizar recursi6n.
Unodelos procedimientos mas comunes y titiles en el procesamiento de
datos, eslaclasificaci6n u ordenaci6n de los mismos. Seconsidera orde-
nar al proceso dereorganizar un conjunto dado deobjetos enuna secuen-
ciadeterminada. El objetivo de este proceso general mente es facilitar la
busqueda de uno 0mas elementos pertenecientes a un conjunto. Como,
ejemploconsiderense laslistas dealumnos matriculados enuna cierta asig-
natura, las listas del censo, los indices alfabeticos de los libros, las guias
telef6nicas, etc. Esto qui eredecir que muchos problemas estan relaciona-
dosdealguna forma con el proceso de ordenaci6n. Es por 10que laorde-
naci6nes un problema importante a considerar.
Laordenaci6n, tanto numerica como alfanumerica, siguelas mismas
reglasqueempleamos nosotros enlavida normal. Esto es, un dato nume-
ricoesmayor 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 deordenaci6n endos categorias: orde-
naci6ndearrays uordenaci6n interna, cuando losdatos seguardan enme-
moriainterna, y ordenaci6n deficheros u ordenaci6n externa, cuando los
datosseguardan en memoria externa, generalmente discos.
En estecapitulo no tratamos de analizar exhaustivamente todos los
metodosdeordenaci6n y ver sus prestaciones de eficiencia, rapidez, etc.;
nosvamosacentrar enlosmetodos mas comunes para ordenaci6n dearrays
y deficheros.
Haymuchas formas de clasificar datos y una de las mas conocidas es la
clasificaci6npor el metodo de la burbuja.
Veamosacontinuaci6n el algoritmo correspondiente, para ordenar una
listademenor amayor, partiendo deque los datos aordenar estan enuna
listaden elementos:
1.- Comparamos el primer elemento con el segundo, el segundocon
el tercero, el tercero con el cuarto, etc. Cuando el resultado deuna
comparaci6n sea "mayor que", seintercambian 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 elementosde
la lista. Con esto conseguimos llevar el valor mayor de estosala
posici6n 0-1.
3.- Repetimos el punta 1, ahora para los 0-2 primeros elementosde
la lista y as! sucesivamente.
4.- El procesotermina despuesderepetir el punta 1,0-1veces,0cuando
al finalizar laejecuci6n del punta 1no hayahabido 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 laprogramaci6n deestealgoritmo para
el caso concreto de ordenar alfabeticamente una lista de cadenas deca-
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 0maximo 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. AZpara 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, seefectua un cambio entre dos ele-
mentos. Si en una exploraci6n a 10largo delalista, no seefectua cambio
alguno, spermaneceni valiendo 0, 10queindica quelalistaesta ordenada,
terminando as! el proceso.
Cuando seanaliza unmetoda deordenaci6n, hay quedeterminar cuan-
tascomparaciones eintercambios serealizan para el caso mas favorable,
parael caso medio y para el caso mas desfavorable.
En el metoda de la burbuja, en el caso mas desfavorable serealizan
(0-1)(0/2)=(0
2
-0)/2 comparaciones, donde 0es el numero de elementos
aordenar. El numero deintercambios es0para el caso mas favorable (lista
ordenada), 3(0
2
-0)/4 para el caso medio y 3(0
2
-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
deestelibro. El tiempo de ejecuci6n es un multiplo de 0
2
y esta directa-
menterelacionado con el numero de comparaciones y de intercambios.
El algoritmo para estemetodo deordenaci6n esel siguiente: inicialmente,
seordenanlos dos primeros elementos del array, luego seinserta el tercer
elementoenlaposici6n correcta con respecto a los dos primeros, aconti-
nuaci6nseinserta el cuarto elemento enlaposici6n correcta con respecto
alostresprimeros elementos yaclasificados y as!sucesivamente hasta lle-
gar al ultimo elemento del array.
478 ENCICLOPEDIA DEL LENGUAJ E 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 a
o
y a
i
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
(n
2
+n-2)/4
(n
2
+n)/2-1
caso medio
caso menos favorable
2(n-l)
(n
2
+9n-1O)/4
(n
2
+3n-4)/2
Para el metoda de inserci6n, el tiempo de ej.ecuci6n es funcion de
0
2
y esta directamente relacionado con el numero de comparaciones yde
intercambios.
El metoda deordenaci6n Quicksort, esta generalmente considerado como
el mejor algoritmo de ordenaci6n disponible actual mente.
1.- Seselecciona un valor del array. Este valor sepuede escoger aleatoria
mente 0haciendo la media de un pequeno conjunto devalores torna-
dos del array. El valor 6ptimo seriaaquel queesteprecisamente enme-
dio del rango devalores (mediana). No obstante, incluso enel peorde
los casos (el valor escogido esta en un extremo), Quicksort funciona
correctamente.
2.- Sedivide el array en dos partes, una con todos los elementos menores
que el valor seleccionado y otra con todos los elementos mayores0
iguales.
3.- Serepiten los puntos 1y 2para cada parte restante hasta queel 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 a
i
<x
a~up con los elementos a
i
>=x
ENDDO
IF ( existe a_in!) THEN
qs( a_in!)
ENDIF
IF ( existe a-----sup) THEN
qs( a-----sup)
ENDIF
END <qs>
A eontinuacion semuestra una version de este algoritmo, el eual se-
leeeionael elemento medio del array, 10eual resulta faeil deimplementar,
aunqueno siempre da lugar auna buena eleeeion. A pesar deella, funcio-
naeorreetamente.
# 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, serea-
lizan o.log 0comparaciones y 0/6.1og 0intercambios, donde 0es 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 de0-1
elementos y otra de 1elemento, el rendimiento es del orden de 0.0=0
2
,
Con el fin de mejorar el caso menos favorable, se sugiere elegir, cadavez,
un valor aleatoriamente 0un valor que sea la median ade 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;
1pila[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;
d o
!
/ * tomar los datos inj, sup de la parte superior de la pila */
inf =pila[pJ .inj, sup =pila[pJ .sup, p--;
d o
! /* 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 segeneran do'
nuevas sublistas. Una de ellas la tratamos en la siguiente iteraci6n y laotra
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 1000elementos de tipo float.
tiempo para ordenar un array que inicialmente ya estaor
denado.
tiempo para ordenar un array que inicialmente esta ordena
do en senti do inverso.
tiempo para ordenar un array que inicialmente yaesta or-
denado, pero, al queseIehan permutado aleatoriamente dos
de sus elementos.
El metoda delaburbuja es el peor de los metodos; el metoda dein-
serci6ndirecta 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
labusqueda de uno 0mas elementos pertenecientes a eseconjunto; aun-
queesposible realizar dicha busqu~>dasin que el conjunto deobjetos este
ordenado, pero esto trae como consecuencia un mayor tiempo deproceso.
Estemetoda de busqueda, aunque valido, es el menos eficiente. Sebasa
encomparar el valor que sedesea buscar con cadauno delos 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 aun programa que
permita buscar un valor en un array previamente leido.
Un metodo eficiente debusqueda, quepuede aplicarse alos arrays c1asifi
cados, es la busqueda binaria. Partiendo de que los elementos del arra}
estan almacenados en orden ascendente, la busqueda binaria consisteen
10siguiente:
Seseleccionael elemento del centro 0aproximadamente del centrodel
array. Si el valor a buscar no coincide con el elemento seleccionado ye5
mayor que el, secontinua labusqueda en la segunda mitad del array.Si,
por el contrario, el valor abuscar esmenor queel valor del elementoselec-
cionado, la busqueda continua en la primera mitad del array. En ambos
casos, sehalla de nuevo el elemento central, correspondiente al nuevoin
tervalo de busqueda, repitiendose el cicIo. El proceso serepite hastaque
seencuentra el valor a buscar, 0bien hasta que el intervalo de busqueda
seanulo, 10quequerra decir queel elemento buscado no figura enel 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 ");
scanJ tt%r: &valor);
if (posicion / = -1)
ptintj(H \ nLa posicion de %g es %d \ n': valor, posicion);
/ * La J uncion 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 J alla */
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 espequeno, esto essi tienepocos registros, sepuedeco-
piar enmemoria enun array yutilizando lastecnicas vistas anteriormente,
ordenar dicho array, copiando a continuaci6n el array ordenado enel fi-
chero. Sin embargo, muchos ficheros son demasiado grandes para tratar
los deesta forma, por 10quepara ordenarlos recurriremos atecnicas espe-
ciales.
EI siguiente programa desarrolla un algoritmo deordenaci6n deun fiche-
ro secuencial, denominado mezcla natural. Lasecuencia inicial deelemen-
tos, vienedada ene1fichero cy seutilizan dos ficheros auxiliares denomi-
nados ayb. Cada pasada consiste enuna fase de distribucion quereparte
equitativamente los tramos ordenados del fichero c sobre los ficherosay
b, yuna fase que mezcla los tramos delos ficheros ayb 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 tramos ordenados en el mismo, por un gui6n ( - ).
fichero a: 1832 - 1442 44 68
fichero b: 1060- 1224 3048
fichero a: 101832 60
fichero b: 12 1424 3042 44 48 68
Observese que s610se 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 sugiereun nuevo ejercicio, que sedeja para queellec
tor 10resuelva, cuyo enunciado es: dados dos ficheros ordenados ay b,
obtener como resultado un fichero c tambien ordenado, que seafusionde
10sdos 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 cpara leer/escribir */
pjc =jopen("c': "w+b");
sjstem("cls' ');
printj ("Pulse AZpara 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)
{
d o
(
/ * 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 */
{
d o
(
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)
{
d o
(
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 =TRUE;
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;
d o
(
/ * 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 1ainformacion sin
tener que copiarla sobre otro fichero y pueden tratarse de forma amiloga
a 10sarrays, 10que simp1ifica enormemente 1aordenacion de 10smismos.
Esto qui eredecir que los metodos expuestos para ordenar arrays, pueden
seraplicados tambien para ordenar ficheros quepueden ser accedidos alea-
toriamente.
El siguiente programa ordena un fichero, enel cual cada registro esta
formado por dos campos: referencia y precio. El desarrollo del programa
variara en funci6n dela estructura delos datos y del tipo del campo (nu-
merico 0alfanumerico) que seutilice para la ordenaci6n del fichero. No-
sotrosvamos aordenar el fichero por el campo referencia, detipo 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));
d o
{
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
Losalgoritmoshash sonmetodos debusqueda, queproporcionan una "100-
gitud de busqueda" pequefia y una flexibilidad superior aladeotros me-
todos, como puede ser, el metodo de"busqueda binaria" querequiere que
loselementos del array esten ordenados.
Por "Iongitud debusqueda" seentiende el numero deaccesos quees
necesario efectuar sobre un array para encontrar el elemento deseado.
Estemetoda debusqueda permite, como operaciones basicas, ademas
delabusqueda deun elemento, insertar un nuevo elemento (si el arrayesta
vacio, crearlo) y eliminar un elemento existente.
Un array producto delaaplicaci6ndeun algoritmo hash sedenomina "array
hash" yson los arrays queseutilizan conmayor frecuencia enlossistemas
de acceso. Graficamente estos arrays tienen la siguiente forma:
El array seorganiza conelementos formados por dos miembros: clave
y contenido.
La claveconstituye el medio de acceso al array. Aplicando alaclave
una funci6n deacceso "fa", previamente definida, obtenemos un numero
entero positivo "i",que nos da laposici6n del elemento correspondiente,
dentro del array.
Conociendo laposici6n, tenemos accesoal contenido. El miembrocon
tenido puede albergar directamente la informaci6n, 0bien un punteroa
dicha informaci6n, cuando esta sea muy extensa.
El acceso, tal cual 10hemos 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 d nL Una funci6n de
acceso, i =fa(dni), que haga corresponder la posici6n del elemento en el
array con el dni, es inmediata:
lacual 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 0y L, mas comunmente entre 1 y L.
En este caso puede suceder que dos 0mas claves den un mismo valor
de "i":
EI metoda hash esta basado en esta tecnica; el acceso al array es direc-
topor el numero "i" y cuando seproduce una colision (dos claves diferen-
tesdan un mismo numero "i") este elemento se busca en una zona deno-
minada "area de overflow".
Estees 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 estalibre, seinserta la clave y el conte-
nido. Si no esta 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 de3, estoes,
Como la posici6n 3 esta ocupada y la clave es diferente, tenemos queincre-
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 deaccesosvienedado
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, esimportante elegir
bien la funcion de acceso.
Una "funcion de acceso" 0"funcion hash" bastante utilizada y que
proporciona una distribucion delasclavesuniforme yaleatoria esla"fun-
cion mitad del cuadrado" que dice: "dada una claveC, seelevaal cuadra-
do (CZ) y se cogen n bits del medio, siendo 2" <= L.
Supongamos: L 256 10que implica n = 8
C 625
CZ 390625 ( 0<=CZ <=2
R
l )
390625
10
00000000000001011111010111 100001Z
n bits del medio: 01011111
z
=95
10
Otra "funcion de acceso" muy utilizada esla"funcion modulo" (resto
deuna division entera):
Cuando seuti liceesta funcion esimportante elegir un numero primo
paraL, con la finalidad de que el numero de colisiones sea pequeno.
Unaalternativa al metoda anterior esladedisponer deotro array separa-
do, parainsertar las claves que producen coli.sion, denominado "array de
overflow", enel cual sealmacenan todas estas clavesdeforma consecutiva.
CLAVE CONTENIDO
5040
3722
2039
6383
Otra forma alternativa mas normal esorganizar una listaencadenada
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, yaqueesto dalugar aquelos elementos inseT-
tados por colision no puedan ser accedidos. Por ello sesuele utilizar un
miembro (campo) complementario que sirvapara poner una marca deque
dicho elemento estaeliminado. Esto permite acceder aotros elementosque
dependendeel por colisiones, yaquelaclaveseconserva ytambien permi-
teinsertar un nuevo elemento en esaposicion cuando sedeuna nuevaco-
lision.
Crear un array hash deuna determinada 10ngitud L que permita almace-
nar losdatos numero dematricula y nombre de10salumnos matriculados
enuna cierta Universidad, utilizando el metoda hash abierto y la funci6n
deacceso 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
i t (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((doub le)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 e1c6digo 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,
yaquee1c6digo y 10sdatos seasignan en segmentos separados. De 10smo-
delosexistentes, 10smode10s de memoria pequenito (tiny) y pequeno (small)
utilizan s01amente dos segmentos, otros mode10s de memoria, discutidos
acontinuaci6n, utilizan mas segmentos.
El mode10utilizado por defecto por e1compilador Microsoft C es el
modelo pequeno (small). Este puede cambiarse desde e1entorno PWB 0
desdela orden de compi1ar emitida bajo e1sistema operativo.
Si e1programa tiene mas de 64K de c6digo 0mas de 64K de datos
asignados estaticamente, uti1izaremos un mode10de memoria diferente a1
modelo pequeno (small).
1. Compilar con 1aorden CL uti1izando 1aopci6n /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 laspalabras clave_near, -J ar,
o _based.
Una delas caracteristicas mas importantes dellenguaje C es quepermite
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
seejecuta, tanto el c6digo como los datos son referenciados por susdirec-
ciones. Estas direcciones pueden almacenarse envariables declaradas como
punteros. El tamafio de estas variables puede ser de 160de 32bits; esto
depende del segmento de memoria donde selocalicen los elementos refe-
renciados.
Los ordenadores queutilizan lafamilia demicroprocesadores 80x86deIn-
tel, gestionan lamemoria ensegmentos de64K. Por 10tanto, para referen-
ciar un elemento senecesita conocer, la direcci6n base del segmentoy el
desplazamiento dentro del segmento.
Ellimite delos64K esnecesario porque losregistrosdelafamilia80x86
sonde16bits; estoquieredecir, queunregistros610puededireccionar 65536
posiciones de memoria (64K).
Segun 10expuesto, una variable detipo puntero que especifique cual-
quier posici6n dememoria, necesita 16bits para ladirecci6n base del seg-
mento y otros 16bits para el desplazamiento dentro del segmento. Enton-
ces necesita un total 32bits.
En la familia 80x86el registro CS contiene la direcci6n base del seg-
mentode c6digo; el registro DS contiene la direcci6n base del segmento
dedatos; el registro SSladirecci6n base del segmento delapila; yel regis-
troESladirecci6nbasedel segmento extra. El microprocesador 80386, tiene
dosregistros adicionales para direccionar otros segmentos: FS yES.
Microsoft C utiliza por defecto el modele de memoria pequeno (small),
quepermiteutilizar 64K para el c6digo y otros 64K para los datos. CUaJ }-
doseejecuta un programa utilizando estemodele, nunca cambian los re-
gistrosCSyDS. Todos los punteros utilizados para referenciar tanto el c6-
digoc6molos datos son de 16bits, porque permanecemos dentro del limite
delos 64K.
Unpuntero de 16bits quereferencia un objeto dentro deun segmento
de64K sedenomina puntero near.
Sinuestroprograma necesita mas de 64K para el c6digo 0para los datos,
necesitaremosutilizar punteros de32 bits, enlugar de 16bits. Estos punte-
rospermitenapuntar acualquier posici6n delamemoria; por ello, reciben
el nombrede punteros far. Las operaciones con estos punteros, (asigna-
cion,modificaci6n, etc.) requieren mas tiempo quelas operaciones con los
punterosnear.
Lasiguientefunci6n presenta enpantalla una ventana rellenada conun ca-
nictercar al cual Ieasociamos un atributo de subrayado, alta intensidad,
parpadeo, video inverso, normal 0una combinaci6n de ellos.
BajoMS-DOS, lamemoria intermedia depantalla con adaptador mo-
nocromoes de 4000bytes, localizados a partir de la direcci6n 0del seg-
mentoOxBOOO. La memoria intermedia de pantalla con adaptador color
gnificoses de4000bytes de longitud en modo texto y de 16384bytes de
longitud en modo grafico, localizados, en ambos casos, a partir deladi
reccion 0del segmento OxB800.
EI atributo de representacion en pantalla selocaliza en la partealta
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:
lacual 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
1columna 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)
lacual indica, que la funci6n escribir debe recibir dos valores de tipo char:
caryatributo. 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 tendria 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-
sionesexplicitas de tipo sobre punteros como seindica acontinuaci6n (ver
Conversion explicita del tipo de una expresion en el capitulo 2).
char ~trib =(char *)&atributo;
Los punteros huge seutilizan para referenciar datos; no sepueden utilizar
para referenciar codigo.
Para un puntero far Microsoft C asume que un objeto, codigo ada-
tos, esta dentro deun segmento de64K; por ello las operaciones aritmeti
cas sobre punteros far serealizan sobre los 16bits queindican el desplaza-
miento. Esta limitacion es superada utilizando los punteros huge; [as
operaciones aritmeticas sobre estetipo depunteros serealizan conlos32
bits correspondientes aladireccion deun elemento dedatos. 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, sonel compilador y el enlazador
de C quienes seencargan del manejo delamemoria. Utilizando punteros
basados enun 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 ladireccion segmentada (segmento mas desplazamiento). EI pun-
terojp esincrementado utilizando solo el valor de 16bits queindicael des
plazamiento. Como una operacion aritmetica sobre 32 bits empleamas
tiempo que una sobre 16bits el trabajo con punteros huge es mas leota
que el trabajo con punteros far.
Un puntero basado enun segmento (based) 9cupa 16bits y tiene lapaten-
cia y flexibilidad de un puntero far.
basados en un segmento (based pointer) se realizan sobre los 16bits 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
dememoria estandar para que sea el compilador quien seencargue 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-J ar; es la forma mas simple de controlar el acceso al c6di-
go y alos datos en un programa; yes la mejor forma de escribir un progra-
ma portable. La desventaja es que con ellos, no siempre seobtiene el c6di-
go mas eficiente.
Memoria max ima
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 dememoria tiene su propia libreria, excepto el modele
enorme (huge) queutiliza laIibreria del modelo grande (large) yel modele
pequenito (tiny) que utiliza laIibreria 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 programaC, hay quetener presentes doslimi
taciones que se aplican a los seis modelos de memoria:
Un unico modulo no puede generar mas de64K dec6digo. 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 dedatos no puede exceder de64K, ano serque
aparezca en un programa compilado bajo e1mode1o de memoria
huge, 0que dicho elemento sehaya declarado con lapalabra clave
J uge.
EI modelo pequenito Iimita un programa a un tamano maximo de64K,
para codigo y datos combinados. La opcion IAT indica al compilador la
utilizacion de este modelo.
Produce un fichero .COM que supera en ve10cidadde ejecuci6na
su equivalente fichero .EXE. EI fichero .COM, es en realidadun
programa generado con e1mode1osmall y enlazado con laIibreria
crtcom.lib con e1fin deIimitar aun unico modulo de64K 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 10sdatos, son accedi-
doscon direcciones near.
EImodelopequeno utiIizaIosdos 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 lAMindica al compilador la utilizaci6n de este
modelo.
Heap
STACK
_BSS
CONST
~ATA
NULL
_TEXT J 64K Maximo
64K Maximo m6dulo_TEXT
--+------ t------.., - CS cargado
_ DS:OOOO,SS:OOO
_ CS:oooo
Por defecto, en el modelo medio, al c6digo seaccede con direcciones
far y a los datos se accede con direcciones near.
EI modelo compacta utiliza un segmento para c6digo y varios segmentos
paradatos. Laopci6n lAC indicaal compilador lautilizaci6ndeestemodelo.
Far Heap
Near Heap
STACK
_BSS
CONST
~ATA
NULL
64K Mciximo m6dulo~ATA _ DS cargado
Por defecto,en el modelo compacto, at c6digo seaccedecon direccio-
nes near y a los datos se accede con direcciones far.
EI modelo grande utiliza varios segmentos para c6digo y varios segmentos
paradatos. Laopci6n IAL indicaat compilador lautilizaci6ndeestemodelo.
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:O O O O
64K Maximo mdoulo_ TEXT _ CS cargado
256 bytes _psp
El significado decada uno delos bloques delas figuras anteriores es
el siguiente:
El stack tiene un tamano de2K por defecto y puede
llegar a64K. Estetamano esfijado entiempo decom-
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 quese
ejecuta un programa. Contiene datos estaticos y glo-
bales no inicializados (excepto -far, _based, 0
_huge). Esto explica por que todas las variables ex-
ternystaticson automaticamente inicializadas acera.
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 8bytes de longitud, (tamano del tipo mas gran-
de; double)para chequear asignaciones apunteros nu-
los, normalmente producidas por no haber asignado
previamente memoria dinamica para el dato. Esteseg-
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.
(programsegment prefix). ,Segmentode256bytesafia-
dido al comienzo del programa. Contiene ladirecci6n
delatabla delasvariables deentorno para el progra-
ma y una copia delalinea de6rdenes deDOS 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 IAHindica al compilador lautilizaci6n deeste
modelo.
El modelo huge esparecido al modelo large, con laexcepci6n deque
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, 0si 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 enbytes igual auna potencia de2. Si el tamafio del
array estaentre 64K y 128K, ambos inclusive, suselementos pueden
ser de cualquier tamafio.
El tamafio enbytesdeunarray huge, esunvalor detipo unsigned long.
Por ello, para calcular el tamafio de un array huge utilizando la funci6n
sizeof( ) que devuelveun valor detipo unsigned int hay queutilizar lano-
taci6n cast.
De forma similar debemos operar cuando realicemos una diferencia
depunteros 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 delamacro 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 secompila 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 secompila con laopci6n IZr (cl /qc IZr prog.c) sueje-
cuci6nproduce la salida:
direcci6nde car:
direcci6nde p:
papunta 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 seyj.
sualiza un mensaje de error sin ninguna indicaci6n de d6nde se ha produ-
cido. La escritura sobre el segmento NULL seproduce independientemente
de que peste 0no inicializado al valor NULL.
En el segundo caso, en cuanto seproduce la primera asignaci6n auna
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 aeste problema es elegir el modelo compact. Sin duda esta es lamejor
elecci6n en cuanto a transportabilidad del programa. Una segunda solu-
ci6n, y quizas mejor en cuanto a velocidad de ejecuci6n, es utilizar el mo-
delosmall que usa punteros near y declarar algunos elementos de datos
comofar.
* 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
queafiadelas palabras clave.-near, -far, _huge, y _based. Estas pala-
brasno pertenecen allenguaje C estandar, sino que son una extensi6n de
MicrosoftC. Susignificado cuando seutilizan con datos 0con c6digo es
el siguiente:
Losdatos son direccionados con 16bits y sealmacenan enel segmento de
datospor defecto. Las funciones son direccionadas con 16bits y sealma-
cenanen el segmento de c6digo actual. Las operaciones aritmeticas con
punteros serealizan con 16bits.
Losdatos son direccionados con 32bits y pueden 10calizarseencualquier
partedelamemoria. Las funciones son direccionadas con 32bits, por 10
quepueden ser llamadas desde cualquier posici6n dememoria. Las opera-
cionesaritmeticas con punteros se realizan con 32bits.
Los datos son direccionados con 32bits ypueden localizarse en cualquier
parte delamemoria. Elementos individuales dedatos (arrays) pueden 50-
brepasar ellimite de los 64K. Estos punteros no sepueden aplicar sabre
el codigo. Las operaciones aritmeticas conpunteros serealizan con32bits.
Los datos pueden localizarse encualquier parte delamemoria ysondirec-
cionados con 16bits mas una direccion base conocida, 10que da lugar a
un direccionamiento con 32bits. Estos punteros no sepueden aplicar 50-
bre el codigo. Las operaciones aritmeticas con punteros serealizan can 16
bits. '
Cuando declaramos punteros utilizando laspalabras clave--'lear, -far,
~uge, 0_based, enalgunos casos puede ocurrir queseanincompatibles
con las funciones dela libreria estandar. En estos casos, utilizando lano-
tacion cast, un puntero near puede convertirse a un puntero far, porque
el compilador afiade ladireccion 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 puedeno
corresponder al segmento de datos por defecto.
Utilizando el modele small siempre podremos pasar auna funci6nel
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 */
}
Ladeclaracion deun objeto 0deun puntero aun objeto puede modificar-
seutilizando las palabras clave: -'lear, -far, --'zuge, 0_based. Cuando
seutilizan estas palabras clavehay quetener encuenta lassiguientes reglas:
La palabra clavesiempre modi fica al objeto 0al puntero situado
inmediatamente a su derecha. Por ejemplo:
Si aladerecha delapalabra clavedeclaramos un objeto, lapalabra
clavedetermina donde seubicanl el objeto: enel segmento dedatos
por defecto (-'lear) 0en un segmento de datos separado (-far,
--'zuge, 0_based). Por ejemplo:
p esun puntero far a un objeto detipo char, que sera almacenado
enel segmento dedatos por defecto independientemente del mode-
10de memoria utilizado.
Si aladerecha delapalabra clavedeclaramos un puntero aun obje-
to, lapalabra clavedetermina el tipo dedireccion que contendra el
puntero: una direccion near (16bits), una direccion far (32bits),o
una direccion huge (32bits).
En la declaraci6n de una funci6n s610sepueden utilizar las palabras clave
_near 0-J ar. Cuando se utilizan hay que tener en cuenta las siguientes
reglas:
La palabra clave siempre modifica la funci6n 0el 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 0far. Por
ejemplo:
jx( ) es una funci6n direccionada con 32 bits que retorna un valor
de tipo char.
Si ala derecha de la palabra clave declaramos un puntero auna fun-
ci6n, la palabra clave determina c6mo sera Hamada la funci6n: uti-
lizando 16 bits (_near) 032 bits (-J ar). Por ejemplo:
La declaraci6n de una funci6n debe coincidir con la cabecera dela
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, -J ar, 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 constante esta en
malloc.h y es de la forma: ((_segment)O).
_NULLOFF Desplazamiento nulo. La definicion para esta constante 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
haintroducido 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
textoreferenciado por el puntero q
La funcion _bmalloc asigna, dentro del segmento especificado, un
bloquedememoria igual al numero debytes indicados. Si no hay suficien-
teespaciodememoria, lafuncion devuelveel valor ~ULLOFF. La fun-
cion _bfree( ) libera el bloque de memoria asignado por la funcion
_bmalloc.
Observar quepara copiar una cadena.de caracteres enotra hemos uti-
lizadola funcion -!strcpy( ), la cual acepta como argumentos punteros
far. Aunquelacompilacion sehahecho bajo el modelo small, no podemos
utilizarlafuncion strcpy( ) porque estaespera como argumentos punteros
near.
Lafuncion_bheapseg( ) da como resultado la direccion base de un seg-
mentocreado 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 0si seejecuta satisfactoriamente 0un
valor -1 si ocurre un error.
Un puntero based puede utilizar como direcci6n base otro puntero. Este
tipo se aplica s610a variables que sean punteros.
El siguienteprograma utilizaunpuntero pb, basado enel punterobase,
para almacenar valores float en el buffer mas adecuado entre variosde-
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[J 600];
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]);
Esteejemplo crea cuatro buffers y elige el mas adecuado para almace-
nar un conjunto de valores.
Ladeclaraci6n deun puntero _based(void) creaun puntero genericoque
actua igual queun desplazamiento dentro deun segmento. Esto quierede-
cir quepodremos definir cualquier segmento y combinarlo conunpuntero
declarado _based(void) utilizando el operador ": >". Este tipo seapliea
s610a variables de tipo puntero.
El siguiente ejemplo utiliza la variable segmento de tipo ----.Segment
para definir el comienzo del segmento; y el'puntero pbcomo 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 esta
representada por dos bytes. El byte de menor peso contiene el caracter y
el byte de mayor peso sus atributos. En el fichero s610salvamos 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 s610a 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"));
printfrscJ ave =%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:
---J lmalloc
_fmalloc
_bmalloc
---J lcalloc
_fcalloc
_bcalloc
_nrealloc
~realloc
_brealloc
---J lfree
J free
_bfree
que vimos en el capitulo de "punteros" con la salvedad del atea dememo-
ria sobre la que trabajan: memoria near, memoria far, 0memoria based,
10cual es trans parente al usuario. Los formatos correspondientes son los
siguientes:
void_based(void) * _bFealloc(segment seg,
void _based(void) *P. size_t t);
Estafuncion cambia el tamafto deunbloque dememoria 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-
riadel bloque.
Si hay insuficiente espacio dememoria lafuncion retorna un puntero
nulo (NULL).
En un ejemplo anterior, no hemos podido utilizar lafunci6n strcpy() por-
que esta espera como argumentos punteros near; en su lugar hemosutili
zado lafunci6n -!strcpy( ), lacual acepta como argumentos punterosfar.
Esta eslaraz6n delaexistencia delas funciones que seenumeran aconti-
nuaci6n:
_fstrcat
_fstrcspn
_fstrncpy
_fstrtok
_fstrnset
_fstrchr
_fstrlen
_fstrrchr
_fstrlwr
_fstrdup
_fstrcmp
_fstrncat
_fstrspn
_fstrupr
_fstrcpy
_fstrncmp
_fstrstr
_fstrset
Estas funciones, salvando laideadequehan sidedisefiadasparaaceptar
argumentos far, tienen el mismo significado que las descritas enel "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 sedescriben a continuaci6n trabajan con areas deme
moria (buffers); entendiendo por area dememoria el espacio ocupadopor
una estructura cualquiera (struct, uni6n, array etc.).
Copia cero 0mas bytes desde un buffer fte a otro des. EI ultimo byteco-
piado es el primer byte de fte que coincida con el byte especificadopor
c, 0cuando sehayan copiado n bytes; la condici6n que sede 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 byteendes si c escopia-
do. En otro caso, retorna un puntero nulo (NULL).
Buscael byte c, en el buffer buf. La busqueda finaliza cuando el byte c
esencontrado 0cuando se hayan explorado Ios n primeros bytes.
Esta fundon retorna un puntero al primer byte que seencuentre en
buf En otro caso, si no aparece c retorna un puntero nulo (NULL).
Estafuncion compara Ios primeros n bytes debun y buf2, distinguiendo
mayusculasy 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
Estafundon tiene el mismo formato y realiza Ia misma fundon que
memcmp(), exceptoqueno hacedistincion entremayusculas yminusculas.
Copia nbytes desdeel buffer fte al buffer des. Si losbuffers fte ydesestan
solapados, el resultado es impredecible.
Copia nbytes desdeel buffer fte al buffer des. Si los buffers fte ydesesi~
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 setrabaja 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 detextos, un compilador, un enlazador, un
depurador, lautilidad Make, un analizador dec6digo fuen-
te, y un sistema de ayuda en linea.
El paqueteMicrosoft C incluyevarios programas deuso general. Entre ellos
estan:
Es el programa quecontrola lacompilaci6n. Permite com-
pilar yenlazar programas fuente. Microsoft C incluyedos
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 muchasdelasop-
timizaciones quepermite el compilador total, pero adife-
rencia deeste, permite lacompilaci6n incremental (opci6n
IGi). La compilaci6n incremental aumenta la velocidad de
compilaci6n yaque el compilador solamente compila aque-
llas funciones que, desde la ultima compilaci6n, han sido
modificadas.
LINK.EXE Programa utilizado para enlazar 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 cada fichero fuente creando el correspondiente fichero
objeto utilizando la orden CL.
3. Se ejecuta la orden LINK para enlazar 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 compilar 0compilar y enlazar uno 0mas ficheros fuente C.
fichero de salida, si no se especifica 10contrario, 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 unas 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 acompilar, del fichero objeto a en-
lazar 0de la Iibreria que se desea pasar al proceso de enlace.
indica el nombre de la libreria (.Lffi) que sedesea pasar al pro-
ceso de enlace.
indica una 0mas 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 Iosficheros fuente especificados creando ficherosob-
jeto (.OBJ ).
2. CL llama al programa enlazador (link) y Iepasa Iosficheros obje-
to resultantes de Ia compilaci6n, mas Ios especificados, maslas
librerias.
3. LINK enlaza estos ficheros objeto, Iaslibrerias especificadas y las
librerias del sistema, y crea un fichero ejecutable (a.exe enel
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 defichero no sehace distinci6n entre maytisculas y mi
miscula,:. Cualquier nombre defichero puede especificarse mediante el ca
mino correspondiente.
Esteejemplocompilaa.c, creando a.ob} ypasaa.ob}, b.ob} ygraphics. lib,
al programa enlazador para crear el fichero ejecutable a.exe.
Cuando no seespecifica el camino para un fichero, CL asume queeste
seencuentra en el directorio actual de trabajo.
Si nosetienelaseguridad dequeel programa coja enlamemoria dis-
ponible,podemos crear overlays. Un overlay esuna parte del programa (m6-
dulo)almacenada sobre el disco; lacual sera cargada enmemoria cuando
serequierasu ejecuci6n. Para especificar que un determinado m6dulo se
vaatratar como un overlay, se incluye entre parentesis.
Esteejemplo crea un programaHamado modJ .exe con un overlay de-
nominado mod2. Cuando durante la ejecuci6n, el control pase a mod2,
estesera cargado en memoria y ejecutado.
Unidentificador de fichero bajo DOS consta de dos partes: un nombre
dehasta 8 caracteres y una ex tension de hasta 3 caracteres separada del
nombrepar un punto.
Significado
CL asume el fichero como un fichero fuente C y 10
compila.
CL asume el fichero como un fichero objeto y 10pasa al
programa enlazador (linker).
CL asume el fichero como una libreria y 10pasa al pro-
grama enlazador.
CL asume el fichero como un fichero objeto y 10pasaal
programa enlazador. Cuando el nombre del ficheronoten
gaextensi6n, estedebefinalizarseconun punto, de10con-
trario, CL asumini la extensi6n .OBJ.
Las opciones comienzan con el canicter "I"0con el caracter "." y deben
utilizarse exactamente igual quesedescriben, esto es, con lamismacombi
naci6n deletras maytisculas y minusculas. A continuaci6n sedescribenlas
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 deHamadaafunciones. 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 afunciones. 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 0iguales que num bytes
en un nuevo segmento. Por defecto num es 256. S610se
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 globales inicia-
lizados.
c6digo de entrada/salida para utilizar en aplicaciones'bajo
Microsoft Windows.
igual que /Gw, pero genera c6digo deentrada mas eficiente.
selecciona la libreria matematica alternativa.
No disponible con la opci6n /qc
selecciona la libreria para emular el 80x87 si este no esta
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
suprime informacion de libreria por defecto
empaqueta los miembros de estructuras y uniones (num
es 1, 2 04)
permite el chequeo de punteros (utilizar solo con /q c)
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 0librerias 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 auna posici6n
de memoria)
eliminaci6n, anivel de bloque, de subexpresiones identi-
cas con el fin de que su evaluaci6n seproduzca una sola
vez
no permite optimizaciones
utilizaci6n delos registros delaUCP para colocar las va-
riables y subexpresiones que seutilizan 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 setrabaja con
valores reales detipos diferentes yaque pueden afectar a
las operaciones de relaci6n.
tamano mas pequeno del c6digo generado
velocidad de ejecuci6n mayor. Opci6n por defecto
no "alias" excepto enllamadas afunciones. 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
l u
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 lasalida producida por el preprocesadoT enlasali-
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 lalista 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
IS scad
IStcad
define el n
O
de caracteres por linea (por defecto 79)
define el nOde 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 1del compilador modelo gran-
de). path especificael camino del ficheroCIL. Utilizar esta
opci6n para compilar programas quegeneran el error' 'out
of near heap space"
IB2path
IB3path
IBATCH
Ic
IHELP
I J
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
enMicrosoft C el tipo char essigned por defecto. Estaop-
cion 10cambia 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 opci6nse
pueden utilizar las opciones IGi (implica ILi) y /Zr. No
sepueden utilizar las opciones: lOw, IGm, IFa, IFc, IFl,
IFs, IFPa, IFPc, IFPc87, IHnum y IZc
indica quejich esun fichero .asmindependientemente de
su extension
indica quejich esun fichero .cindependientemente desu
extension
igual que IW1. Es la opcion por defecto
suprime los mensajes deaviso del compilador (warnings)
indica el nivel de mensajes de error a visualizar. n esun
valor 1, 2, 3, 04. A mayor nivel, mayor exigenciaenel
amilisis sintactico durante la compilacion
esel mayor nivel demensajes deerror. 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 laslibrerias apropiadas, para crear un fichero ejecu-
table. La sintaxis de esta orden es la siguiente:
indica uno 0mas ficheros objetos que sequieren 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 sequi erecrear. La ex-
tension asumida por omision es .exe.
esel nombre del fichero 0mapa decarga, el cual contiene una
entrada para cada segmento delos modulos de entrada. Cada
una delas entradas muestra tambit~nel desplazamiento dentro
del fichero ejecutable. Este fichero no secrea a menos que se
solicite especificamente. Si no seintroduce nada el nombre oe
fichero reservado es NUL que indica que no secreara ningun
mapa.
Sepuede especificar tambien un nombre dedispositivo para di-
rigir lasalida aeste, como por ejemplo: AUX para un dispositi-
vo auxiliar, CON para la consola (terminal), PRN para la im-
presora.
lib indicauna 0mas librerias (stand-alone) 0directorios donde pue-
den ser localizadas separadas por signos +0por espacios.
opciones pueden ser cualquiera delas siguientes (laparte incluida entre
corchetes es opcional):
IBA IBA[TCH]
Suprime lasalidapor pantalla demensajes, dellogotipo, depre-
guntas 0de 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-
dosconestaopcion debencompilarseprimero conlaopcion IZi.
ICP ICP[ARMAXALLOC]:num
Maximo numero deparrafos 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 losda-
tos definidos para estar en la parte mas alta del segmento de
datos. Por defecto, link carga los datos comenzando por lapar-
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 derefe-
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 seutiliza
con la opci6n IPACKCODE.
IHE[LP]
Petici6n de ayuda.
IHI[GH]
hace que el cargador coloque el fichero ejecutable 10mas alto
posible en la memoria. Esta opci6n seutiliza 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 0ficheros objeto incluyan numeros delinea (op-
ciones del compilador IZi 0IZd), de10contrario laopcion ILl
sera ignorada.
IM[AP]
Crea un fichero que contiene una lista clasificada detodos los
simbolos globales definidos enel fichero 0ficheros 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 seespecifica lib, LlNK bus-
ca en todas las librerias excepto en esta.
INOE[XTDICTIONARY]
estaopcion previeneal programa enlazador debuscar enel dic-
cionario queel mismo mantiene. Normalmente el enlazador con-
sulta estediccionario (lista delocalizaciones desimbolos) para
acelerar labusqueda delibrerias. Laopcion INOE lautilizare-
mos cuando exista una redefinicion de un simbolo.
INOF[ARCALLTRANSLATION]
Si laconversion dellamadas -far allamadas _near estaacti-
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 10hace laopcion 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 1a 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. numindica 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 eI fi
chero .ex e en un disco flexible. Esto per mite insertar un nuevo
disco para almacenar el fichero .ex e.
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 delapila para el programa. num escual-
quier valor positivo (decimal, octal 0hexadecimal) no superior
a 65535. El tamafto por defecto es de 2K.
IT IT[lNY]
Indica al enlazador que sevaacrear 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
ejecutableprog.exe, almacenado en el directorio source y optimizado en
tamafto y velocidad (opciones IE y IF). Tambien secreael mapa decarga,
prog.map, correspondiente.
En el siguiente ejemplo, seenlazan los ficheros progJ .ob}, prog2.ob}
y prog3.ob} utilizando lalibreria libutil.lib. El resultado esel fichero deno-
minadoprogI.exe por defecto. El mapa decarga correspondiente esHama-
do mapaen.map.
Unasegunda forma deenlazar uno 0mas programas esejecutando laor-
denLINK 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 0pro-
gramas a enlazar separados por signos +0por 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 aNUL; laextension .map seasume.
A la cuarta pregunta se responde con una 0mas librerias (stand-alone) 0
con nombres de directorios don de pueden ser localizadas, separadas par
signos +0por 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, sepone 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 ala 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
esespecialmente uti! cuando hay que especificar listas largas de m6dulos
objeto y ademas sepreve, por los motivos que sean, que el proceso deen-
lace, seguramente, habra que realizarlo mas de una vez.
Antes deutilizar estaopci6n debecrearseun fichero de respuesta auto-
matica. Este contiene varias lineas de texto, cada una de las cuales es la
respuestaaunapregunta del enlazador. Estas respuestasdebenir enel mismo
ordenquelaspreguntas del enlazador ysiguenlas mismas reglas expuestas
anteriormente.
Para especificar un fichero derespuesta automatica seprocedera como
seindica a continuaci6n:
a+b+c+d
/PAUSE IMAP
list
xlib.lib;
Esta orden Ieindica al programa enlazador que procese los ficheros
objetoa, b, c y d para producir un fichero ejecutable Hamado a.exe y un
mapalist.map. Laopci6n IPAUSE indica aLINK que haga una pausa an-
tesdeescribir el fichero ejecutable para poder cambiar el disco flexible,
si esnecesario. Laopci6n IMAP indica que secreeel mapa decarga para
el ficheroejecutable resultante dela operaci6n deenlace. Tambien sepro-
duceel enlace con las rutinas necesarias de la libreria x/ib./ib.
Sinosetienelaseguridad dequeel programa coja enlamemoria disponi-
blepodemos crear overlays. Un overlay esuna 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 0ILi). 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; yaque lUNK
trabaja sobre un fichero .exeexistente (no .com), el cual desearnos modificar.
fich.exe es el nombre del fichero ejecutable que se quiere crear. Laex-
tension asumida por omision es .exe.
objeto indica uno 0mas ficheros objetos que se quieren enlazar sin
la extension .obj y separados por espacios.
IA indica a IUNK que verifique si han ocurrido cambios en101
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 seinvoca
LINK lINe.
IX si esta disponible un manejador de memoria expandida, esta
opd6n hace que ILINK no 10utilice. Por defecto, ILINK uti-
liza memoria expandida.
LIBRERIAS Y
UTILIDADES DEL COMPILADOR
Unadelas caracteristicas mas importantes deC esla facilidad con laque
sepueden construir y utilizar librerias de funciones. Una libreria es una
coleccionde funciones precompiladas y agrupadas bajo un mismo nom-
bre(fichero) quepueden ser utilizadas por cualquier programa. Laventaja
deutilizar tales librerias esta en no tener que escribir el codigo fuente de
lafuncion (de libreria) en el programa y en el tiempo que se ahorra, ya
queel compilador cuando compila nuestro programa no tiene quecompi-
lar tales funciones.
Cproporciona lautilidad LIB para crear librerias quellevancomo ex-
tension.LIB ypara mantener lasyaexistentes. Estas sondenominadas tam-
bienlibrerias stand-alone 0librerias independientes (cuando son requeri-
dasel sistema las llama automaticamente). Estas librerias tienen el mismo
formate que las suministradas con C y con otros lenguajes de alto nivel
deMicrosoft.
Enestecapitulo, ademasdelaslibrerias, veremosotras utilidades como
NMAKE y Code View por citar algunas de ellas.
Si seescribe un punta y coma (;) acontinuacion del nombrede
la libreria, Lffi ejecuta solamente un chequeo sobre lalibreria
especificada. Si seencuentra algun modulo invalido seescribe
un mensaje de error. Este chequeo normal mente no esnecesa-
rio, ya que Lffi chequea cada fichero objeto antes de afiadirlo
a la libreria. Por ejemplo:
EI manejador delibrerias Lffi permite crear ymantener librerias .LIB. Un
programa que llama arutinas deuna libreria es enlazado (link) con laIi-
breria para producir el fichero ejecutable. Solamente son enlazadas lasru-
tinas necesarias, no todas las rutinas de la libreria.
Una libreria stand-alone estahecha abasedemodulos objeto quehan
sido combinados para formar lamisma. A diferencia deun fichero objeto,
un modulo objeto no existeindependientemente delalibreria alaqueper-
tenece y no tiene un camino 0extension asociada con su nombre.
Extraer modulos deuna libreria existentey 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 sedesea crear 0modificar.
Si la libreria no existe, LIB pregunta si sequiere crear. Por de-
fecto se asume la extension .LIB.
IH[ELP]
peticion de ayuda.
II[GNORECASE]
indica aLIB, cuando compara nombres, queno haga distincion
entre minusculas y mayusculas. Utilizar esta opcion cuando se
combine una libreria creada con laopcion 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 aLIB queno genereun diccionario. Esteesuna parte ex-
tra de lalibreria que mas tarde dara lugar a una mayor veloci-
dad en el proceso de enlace de la misma. Utilizar esta opcion
cuando sepresente el error out of memory. Otra posibilidad es
dividir la libreria en otras mas pequefias.
INOI[GNORECASE]
indica aLIB, cuando compara nombres, que distinga minuscu-
lasymayusculas. Utilizando estaopcion, nombres como fUDcA
y fUDca pueden ser puestos en la misma libreria.
Cuando secreauna libreria con estaopcion, LIB marca lalibre-
ria internamente para indicarlo. Cuando secombinan varias 1\-
brerias y una deellas 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 esuna po-
tencia entera de 2cuyo 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 lalibreria. Los modulos en lalibreria son siempre alineados
para comenzar en una posicion que sea un multiplo del tamafio
de la pagina.
+{fich-obj/lib J afiade ala libreria el fichero objeto especificado. Si laex-
tension no seespecifica seasume .obj. Si seespecifica 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 aun fichero objeto del mis-
mo nombre.
-*modulo mueve el modulo especificado aun 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 laIi-
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 decada
modulo. El nombre de cada modulo va seguido por unalis
ta alfabetica de los simbolos globales 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 seespecifica secrea una copia desegu
ridad delalibreria original con extensi6n .bak, permaneciendo
con el mismo nombre la modificada.
Reemplazar el m6dulo fund enlalibreria lib_a. lib por el fichero ob-
jetodel mismo nombre funcl.ob}.
EI mismo resultado seobtendria con cualquiera delas dos 6rdenes si-
guientes,yaque las operaciones deborrado seefectuan siempre antes que
lasoperaciones de anadir, indiferentemente del orden en el que sehayan
especificadoen la linea de 6rdenes.
EI siguiente ejemplo mueve el m6dulo fund de la libreria lib_a. lib
aunfichero objeto denominado fund.ob}, con 10que este modulo desa-
parecedelalibreria. Copia el m6dulo func2 aun fichero objeto denomi-
nadofunc2.ob}, estem6dulo permanece enla libreria. La libreria modifi-
cadaesHamadalib_b. lib permaneciendo sincambiar lalibreria lib_a. lib.
LIB lib_a -fund +fund;
LIB lib_a +fund -fund;
Library Name:
Library does not ex ist. Create? (y/n)
Operations:
List File:
Output .library:
Lasegunda pregunta s610nos esformulada cuando no existelalibre-
ria especificada. Laquinta s610nos es formulada si existelalibreria espe-
cificada.
Si aalguna deestas preguntas, excepto ala primera, seresponde con
Enter, seasume para ellalarespuesta por defecto y si seresponde conun
punto y coma seasume para ella y para las siguientes las respuestas por
defecto.
Larespuesta por defecto para List File es NUL y para Output library
es la especificada en Library Name.
Igual que seindic6 para laorden LINK, para laorden LIB sepuedecrear
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 ejempl0crea una libreria Hamada milib.lib con 10sm6dulos
mayor y cambiar correspondientes a10sficheros mayor.obj y cambiar.obj.
Los pasos a seguir son 10ssiguientes:
/* 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;
/ * Permuta 10svalores 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 10spasos necesarios para obtener un fichero ejecuta-
bleen 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
delaultima vez que semodifieD el fichero, 10cual exige introducir la fecha
correctamente al iniciar cada sesion.
Cuando seejecuta 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 0mas bloques y cada bloque
esta formado por una linea de dependencia seguida por una 0mas Iineas
deordenes. 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 1y el sangrado de al menos un espacio en las lineas 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-
vesy separados por puntos y comas.
Un fichero es dependiente si cuando cambia causa una modificacion
en 10sficheros 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 desde el simbolo # hasta el final de
la linea. Un comentario puede colocarse solo en una linea apartir decual-
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 enlas
lineas de 6rdenes 0a partir de la columna 1de 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 deprogl60J .c es mas reciente que prog1601.obj.
Si es mas reciente, 0si progl60J .obj no existe, ejecuta la orden:
Esta orden crea un fichero progl601.obj nuevo, el cual es ahora mas
reciente que progI60J .exe, 10que 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 apartir 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 seutilizan 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 ficherosde-
pendientes hayan sufrido modificaciones, 0no.
Ie suprime los mensajes de derechos de copia, 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 10quepuede
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 seespecifica, 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 quehan variado respecto al fichero re-
sultante; encaso afirmativo retorna un valor distinto decero.
En caso contrario devue\veun O.
suprime lavisualizaci6n delas 6rdenes del fichero dedescrip-
ciones cuando son ejecutadas.
cambia las fechas de los ficheros que han variado a la fecha
actual.
Las macros seutilizan 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
seejecute 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 dedescripciones emplearemos
la siguiente sintaxis:
Si el nombre delamacro esdeun 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 estama-
cro sustituimos enlalinea dedependencia laextension .C por laextension
.OBJ. De este modo NMAKE ejecuta la siguiente orden:
NMAKE provee varias macros predefinidas. Algunas de eHasson lassi-
guientes:
nombre del fichero resultante que NMAKE esta procesan-
do. Esta macro solamente esutilizada enlalineadedepen-
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 aNMAKE. 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 ala 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 $? $@
Lamacro especial $$(@F) indica el nombre (sin path) del ficherore-
sultante actual. NMAKE primero chequea si GLOBALS.H, en el directo-
rio actual, havariado con respecto al fichero resultante C: \ C600 \INCLU-
DE \ GWBALS.H Si havariado ejecutalaordenCOPY. El procesoserepite
para el resto de los ficheros.
Estos ultimos ejemplos demuestran como NMAKE puede utilizarse
para realizar otros trabajos diferentes al demantenimiento deprogramas.
Lasreglas deinferencia son plantillas desdelascuales NMAKE decideque
hacer con un bloque de descripci6n cuando no contiene ficheros depen
dientes u 6rdenes. Una regIadeinferencia sedefine delasiguienteforma:
orden representa las 6rdenes para construir los ficheros conexten
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 $<
Lamacro $<representa el nombre deun 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
queespecifique como crear el fichero resultante desde Ios ficheros depen-
dientes. Si no existeun fichero dependiente, NMAKE busca una regIade
inferencia que especifique como crear Ia dependencia 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, IaregIa de inferencia es aplicable a ficheros depen-
dientescon extension.oBi yficheros resultantes conextension.EX. Como
enel bloque 1 no hay ordenes NMAKE aplica IaregIadeinferencia reem-
plazando $<por PROGOI.OBJ . En cambio.' como en el bloque 2hay or-
denes NMAKE no aplica Ia regIa de inferencia.
Las reglas deinferencia solamente son titilescuando existeuna corres-
pondencia, uno auno, entre ficheros con extension desde_ext y ficheros
conextension a_ext. Las reglas deinferencia eliminan Ianecesidad depo-
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) 0en 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, seejecutan las sentencias entre !ELSE
y!ENDIF
!IFDEF macro si la macro esta definida, seejecutan las sentencias en
tre el siguiente !ELSE 0!ENDIF.
!IFNDEF macro si la macro no esta definida, seejecutan las sentencias
entre el siguiente !ELSE 0!END IF.
CAPITULO 16: LIBRERIAS Y UTILIDADES DEL COMPILADOR 589
!CMDSWITCHES {+I-lope
activa (+) 0desactiva (-) 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 leey evalua el fichero reglas.txt. Activalaopci6n ID de
NMAKE, lacual visualizalasfechasdelosficheroschequeados. Comprueba
si la macro DEBUG esta definida y si 10esta y su valor es "s" invoca al
enlazador con la opci6n Ica y si su valor no es "s" invoca al enlazador
sinlaopci6n ICO. Si no estadefinida visualiza un mensaje deerror y fina-'
liza NMAKE.
NMAKE IF prog1602.mak DEBUG=s
NMAKE prog1602.mak
Si una macro esta definida en mas deun lugar, la ejecuci6n de la misma
sehace en el primer lugar que se encuentre al aplicar el siguiente orden
de prioridades:
1 linea de 6rdenes
2 fichero de descripciones 0fichero a incluir
3 variables de entorno
4 fichero TOOLS.INI
5 macros predefinidas
Cuando necesitamos emitir una orden con una listadeargumentos queex-
cede de la longitud maxima impuesta par DOS (127caracteres), la solu-.
ci6n esgenerar un fichero enlinea. NMAKE puede generar ficheros enli-
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 enun
fichero enlineanombrado jich_l. Si el nombre seomite secreaunfichero
temporal; enotro casa, el fichero seratemporal si sefinaliza conNOKEEP
(opd6n por defecto), 0permanente si sefinaliza conKEEP. En cualquier
caso NMAKE s610 crea 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;

Esteejemplo creaun fichero temporal equivalente aun fichero deres-


puesta automatica para la orden LINK.
Conlaorden NMAKE sepueden utilizar los caracteres asterisco (*) einte-
rrogante (?) para especificar ficheros engeneral. Otros caracteres especia-
les son los siguientes:
simbolo deescape. Esutilizado para queNMAKE interprete
los caracteres especiales que se describen a continuaci6n
como caracteres normales.
Ladescripci6n deun fichero puede utilizar una sintaxis especial para de-
terminar cada uno de sus componentes. Esta sintaxis es:
OJ os representa la especificaci6n completa del primer fichero de-
pendiente.
% IparteF representa una componente. parte esuno delos siguientes ca-
racteres:
p camino (path)
d unidad (drive)
O J o s
%IpF
%IdF
%IfF
%I eF
C: \ C600 \ SOURCE \ PROG.C
C: \ C600 \ SOURCE
C:
PROG
.C
Un pseudoobjetivo es un objetivo que no es un fichero en un bloquede
descripci6n. Un pseudoobjetivo esun nombre que sirvepara construir un
grupo de ficheros 0ejecutar un grupo de 6rdenes. Por ejemplo:
AL--.DIA: *.C
ICOPY $** A: \PROYECTO
Cuando NMAKE evaluaun pseudoobjetivo siempreconsidera que10s
ficheros dependientes han variado. En el ejempl0anterior, NMAKE copia
todos 10sficheros con extensi6n .C alaunidad 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 sepresenta 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 desde 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 10tanto, en 10sucesivo, 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 seha definido lamacro DEP seestable-
cen las opciones de compilaci6n y enlace que permitan depurar
el programa.
3. Si enlaorden NMAKE seha definido lamacro INC seestablecen
las opciones quepermitan lacompilaci6n yel enlaceincremental.
4. Verifica que las macros DEP eINC no hayan sido definidas si-
multaneamente. Esta acci6n se considera como un error.
5. Si enlaorden NMAKE hemos definido alguna delas macros an-
teriores, lasreglasdeinferenciapor defecto, seranmodific~das con
las nuevas opciones; enotro caso lasopciones por defecto perma-
neceran inalteradas.
Por ultimo hemos escrito dos bloques para mantener un supuesto pro-
yecto, PROYEC16, compuesto por una funci6n principal MAINC yuncon-
junto de funciones auxiliares FUNC16*.C. El primer bloque genera unfi-
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 deMicrosoft esun programa interactivo quenos
ayudara alocalizar errores rapidamente. Code View puede utilizarse sola-
mente con ficheros .exeno sepuede utilizar con ficheros .com.
Cuando depuramos un programa esbueno tener presente lasiguiente
consideraci6n: el compilador C permite escribir mas de una sentenciaen
una misma linea. Por ejemplo:
Esta forma de escribir un programa dificulta la depuraci6n, yaque
no esposible acceder alas sentencias individuales que componen lalinea.
Por ello, serecomiendaescribir lassentenciasanteriores delasiguienteforma:
car =texto[i];
if (car == <\ n')
+ +lineas;
Cuando un programa que sedesea depurar secompila sedebe 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 delinea, pero no informacion extra, con 10 queel 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.
Esteejemplo compila yenlaza el fichero fuenteprogOJ.c. EI resultado
esun 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 lainformacion total necesaria para el depurador; com-
pila modulo02.c produciendo un fichero objeto con informacion limitada.
A continuacion seutiliza CL otra vez para enlazar los ficheros objeto re-
sultantes. Esta vez, CL no compila de nuevo, ya que los argumentos no
tienen extension. Laopcion IZi enestaultima orden hacequeel enlazador
(linker) seainvocado con laopcion ICO. El resultado esun 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 0la
memoria extendida si XMM esta instalado.
Inn inicializa el modo de25lineas (nn=25), de43 lineas (nn=43)
o de 50lineas (nn=50).
/C6rdenes permite especificar una 0mas 6rdenes que seejecutanin in-
mediatamente despues de invocar a CV.
ID[Kb] ejecuta CV empleando latecnica deoverlays con el findedis-
poner demas espacio para el programa. Kb esun valor (mul-
tiplo de 16)entre 16y 128queespecifica 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 sedepure 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 2a24 (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 0Shift +F6.
El area tercera ocupa la linea 25 y es utilizada para indicar las accio-
nes que podemos tomar.
[ BP +f l f l f I ' I l
[ BP +f l f l f l 81
[ BP +f l f l f l Cl
II
I f 1:
11:
12:
13:
1'1:
IS:
f l o a t
f l o a t
f l o a t
l o c a l
x = 1. f I f I f I f I f I
y = 2. f I f I f I f I f I
z =3. f I f I f I f I f I
irl" ,;S"' ...:i.'WIl
r eg
=f l f I f I &
=I f 11A
=f l f I Bl
=' 1f 1f 1f 1
=I f 11&
=I f 12f 1
SI = f I ' I 82
01 =f I ' I 82
OS =3f 1C&
ES =2Bs O
SS =3f 1C&
CS =2B7D
I P =f l l ZE
f L =f l 2f 12
el s e
Md z:
el s e
if ( y > Z )
Ma y:
else
Mo r y l by t e OS: f I f I f I f I ( ACT I VE)
CO 2f 1 C& ' 1f 1f I f I 9A f f l f E 10 f f l 8B f l 2 f I f I 2A 51 f l 3
f I f I 2A BB f l A f I f I 2A ' 1C 2B f l l f l l f l l f I f I f l 2 f l 3 f f f f
f f f f f f f f f f f f f f f f f f f f f f f f SO 2B A2 2&
t-------------<:coMMand
Las operaciones minimas que debe incluir un depurador son lassi-
guientes:
Permite ver lasentencia del programa que esejecutada. Code View
incluye las siguientes opciones:
Ejecutar una sentencia cada vez, incluidas funciones definidas
por el usuario. Esta modalidad seactiva ysecontinua pulsando
lateclaF8. Si no queremos quelasfunciones seejecuten senten-
cia a sentencia pero si la funci6n principal main( ) utilizamos
la tecla FIO.
Un punta deparada esuna pausa que sehace enun lugar determi
nado dentro del programa. Esto permite comprobar los valoresde
las variables en ese instante. Colocar los puntos de parada donde
se sospeche que esta el error.
Para poner 0qui tar una pausa secoloca el cursor enellugar donde
vaatener lugar lapausa y sepulsa F9. Sepueden poner tantos pun-
tos de parada como necesitemos.
Las ventanas local y watch permiten observar los valores delas va-
riables y de las expresiones especificadas. Al ejecutar el programa
paso apaso podemos observar enestasventanas los valores quevan
tomando las variables.
Si pulsamos latecla F5 laejecuci6n continua hasta el final del pro-
grama 0 hasta el primer punta de parada, si existe.
Cuando sepulsa F7 el programa seejecuta desde laultima senten-
cia ejecutada hasta la linea donde seencuentra el cursor.
Si sedeseaprocesar el programa paso apaso deuna forma automa-
tica, ejecutar laorden Animate del menu presentado por laopci6n
Run. Una vez iniciada laanimaci6n deun programa puede ser de-
tenida pulsando cualquier tecla. Lavelocidad deejecuci6n automa-
tica sepuede aumentar 0disminuir mediante laorden Trace Speed
del menu Options.
Ejecutar laorden Restart cada vez que iniciemos denuevo el proceso
de depuraci6n.
Para salir del depurador ejecutar la orden Exit del menu presentado
por la opci6n File.
Cuando seentra enCode View, mediante laorden CV, 10 primero queapa-
receesel menu principal y tres ventanas: laventana 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 0color
diferente (es indiferente el utilizar mayusculas 0minusculas). 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 0en 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 ala letra que sepresenta en alto bri-
1100color diferente (es indiferente el utilizar mayusculas 0minus-
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 0combinaci6n 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 laor
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. Seapunta a la opcion deseada del menu y sepuisa el boton iz-
quierdo del raton.
Para enrollar/desenrollar el texto sobre la ventana activa sedispone
de una barra de scroll vertical y otra de scroll horizontal. Ambas tienen
ensus extremos unas flechas que indican el desplazamiento imaginario de
lapantalla sobre el texto cuando seefectua laaccion descroll y un cursor
quesedesplaza a10 largo delalinea descroll, queindica laposicion relati-
va del cursor de pantalla con respecto a los limites del texto.
1. Para avanzar 0 retroceder una linea seapunta alaflecha superior
oinferior delabarra descroll vertical ysepulsa el boton izquier-
do del raton. Para desplazar el texto una posicion a la izquierda
o aladerecha seapunta ala flecha derecha 0 izquierda delaba-
rra de scroll horizontal y sepulsa el boton izquierdo del raton.
2. Para avanzar 0retroceder una pagina, secoloca el cursor del ra-
ton sobre la linea de scroll vertical, entre el cursor de scroll y la
flecha inferior 0entre el cursor de scroll y la flecha superior y se
pulsa el boton izquierdo del raton. La operacion esanaloga 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 seapunta al cursor de scroll vertical de la ventana de texto y
setira, con el boton izquierdo del raton puisado, hacia arriba 0
hacia abajo arrastrandolo sobre lalinea descroll, el texto sedes-
plaza hacia abajo 0hacia arriba. La accion sevecuando sedeja
depulsar e!boton. Esta misma operacion sepuede realizar sabre
lalineadescroll horizontal paradesplazar el textohacialaizquierda
o hacia la derecha.
Es posible variar el tamafio deuna ventana. Para ello, seapunta ala
linea deseparacion entre ventanas y con el boton izquierdo del raton pul-
sado setira enladireccion apropiada para agrandar 0 reducir laventana.
Para activar una ventana, apuntar acualquier lugar dentro delamis-
ma y pulsar el boton izquierdo del raton.
Para activar 0 desactivar cualquier accion dentro de una ventanade
dhilogo, apuntar al espacio entre corchetes 0 entre angulos y pulsar el ba-
ton izquierdo del raton.
Muchas de las ordenes que seexponen a continuacion requieren laselec-
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 aseleccionar ymanteniendo pulsada latecla Shift, marcar el tex
to desplazando e1cursor con las teclas de movimiento.
Tambien esposible seleccionar texto con el raton. Para ello, seapunta
al principio del textoaseleccionar ymanteniendo pulsado el boton izquierdo
se tira en la direccion deseada hasta marcar el texto.
Cuando seejecuta esta orden senos pregunta por el nombre del nuevofi-
chero quedeseamos cargar enlaventana source. Este fichero puedeserun
fichero fuente, un fichero .h 0 cualquier otro fichero detexto. Cuandose
acabe dever e1fichero cargado sepuede volver acargar el fichero original.
Permite se1eccionar uno de10sm6du10s fuente quecomponen e1programa
y cargarlo en 1aventana source.
Permite escribir todo 0parte del fichero visualizado en 1aventana activa
a un fichero 0dispositivo especificado (por ejemp10LPTl).
Estaorden permite salir tempora1mente a1DOS pudiendo as!ejecutar cual-
quier otra tarea bajo e1sistema operativo. Lavuelta a Code View sehace
ejecutando 1aorden Exit.
Deshace 10scambios efectuados en 1alinea que estamos editando. No es
posib1evo1vera una linea ya abandonada y restaurarla.
Copia e1texto se1eccionado de1aventana activa y10salvaenuna memoria
intermedia, permaneciendo en 1amisma 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 enla
ventana activa y ejecutar Paste.
Insertar texto: mover el cursor a la posici6n deseada dentro de laven-
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 delostres
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 estaventana no sepueden afiadir ni borrar variables. Si sepermite
modificar el valor de una variable.
Activa/desactiva laventana watch. Esta venta:namuestra los valores delas
variables y expresiones seleccionadas.
Para afiadir una variable 0 una expresion alaventana watch, ejecutar
la orden "Add Watch" del menu Watch.
Es posiblemodificar el valor deuna variable 0 deuna expresion incluida
encualquiera deestas ventanas. Esto sehace 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 laventana, local 0watch, estaactiva. 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 dedatos, apuntar al mismo can
el raton y pulsar dos vecesel boton izquierdo; 0bien, mover el cursor al
elemento de datos y pulsar Enter.
Activa/desactiva la ventana command. Esta ventana permite emitir orde-
nespara Code View. Estas ordenes nos permiten hacer deuna forma direc-
ta las operaciones que realizamos a traves de los menus.
Activa/desactiva laventana help. Estaventanavisualizainformacion deayu-
da a cerca de Code View y palabras clavedel lenguaje.
ejecutar help
pulsar Shift + Fl
situar el cursor sobre una palabra clavedellenguaje, sobre una Of-
den de un menu 0sobre una accion en una ventana de dialogo y
pulsar Fl.
Esta orden hace que sevisualice lapantalla delos resultados. Para volver
a la ventana de texto pulsar F4.
Esta orden permite agrandar la ventana activa si esta atamafio normal 0
ponerla atamafio normal si estaagrandada. Cuando laventana estaagran-
dada aparece sobre el menu View la orden Restore en lugar deMaximize.
Para realizar esta operaci6n con el rat6n, apuntar alacajita delaes-
quin.asuperior derecha delaventana ypulsar el bot6n izquierdo del rat6n.
Permite modificar el tamafio dela ventana activa utilizando las teclas de
movimiento del cursor. Parafinalizar estaoperaci6n pulsar Enter; paraaban-
donar esta operaci6~ sin modificaciones, pulsar Ese.
Esta orden cierra laventana 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 estaorden para buscar enlaventana activa lasiguiente 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 serequiereutilizar expresionesregularespueden utilizarsecomo
comodines los siguientes caracteres:
lalocalizaci6n del texto escrito despues deestecanicter debedarse
al principio de una linea.
la localizaci6n del texto escrito antes de este canicter debe darse
al final de una linea.
[ ] localizar uno delos caracteres del conjunto especificado entrecor-
chetes. Dentro delos corchetes pueden utilizarse los caractereses-
peciales:
para indicar los limites del conjunto decaracteres queseae
sea especificar.
\ cualquier canicter delosindicados precedido por estecaracter pierde
su significado especial y esconsiderado 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 0por
Select Text.
Esta orden seutiliza para buscar una etiqueta 0un nombre deuna funci6n
en un programa en lenguaje ensamblador.
Esta orden hace que la siguiente sentencia a ejecutar sea denuevo la pri-
mera sentencia delafunci6n main( ). lnicializa todas las variables que in-
tervienen enel programa. Ejecutar estaorden antes deuna nuevaejecuci6n.
Ejecuta el programa paso apaso automaticamente desdelainstrucci6n ac-
tual. Si el programa yaha side ejecutado anteriormente hay que ejecutar
previamente laorden Restart. Una veziniciada laanimaci6n deun progra-
mapuede ser detenida pulsando cualquier tecla. Lavelocidad deejecuci6n
automatica sepuede aumentar 0disminuir mediante laorden Trace Speed
del menu Options.
Esta orden permite especificar los argumentos enlinea de6rdenes necesa-
rias para la ejecuci6n del programa.
Esta orden permite registrar lahistoria del proceso de depuraci6n 0utiIi-
zar lahistoria yaregistrada. Durante esta operaci6n secrean dos ficheros
conunnombrebase(prog) igual al nombredel 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 lahistoria deun proceso dedepuraci6n si laorden
History On esta activa.
Para repetir parte delahistoria, desde el principio hasta un determi-
nado punto, primero situarseenel punta delahistoria hasta el cual sequiere
repetir el proceso dedepuraci6n yacontinuaci6n ejecutar laorden Replay.
Estaorden permiteafiadir enlaventana watch variables y expresiones cuyo
valor sequiere visualizar. Losvalores senin visualizados cuando seejecute
el programa paso a paso.
Una expresi6n puede ser una variable decualquier tipo 0una expre-
si6n Cvalida. Una expresi6n derelaci6n dara un resultado falso (0) 0 ver-
dadero(l).
Para visualizar una expresi6n deestas con formato desalida diferente
al indicado enel 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 delaven-
tanaWatch. Para borrar una expresi6n, seleccionarla y pulsar Enter. Para
borrar todas la expresiones elegir la opci6n Clear All.
Esta orden permite poner un punta deparada. Un punta deparada esuna
pausa que sehaceenun lugar determinado del programa. Esto nos permi-
teverificar los valores quetienen las variables eneseinstante. 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 laacci6n y pulsar F9 0ejecutar la orden Set Breakpoint.
Paraquitar un punto deparada, situar el cursor enellugar dondeestapuesto
y pulsar F9.
Define el formato con el quesevisualizara una zona dememoria enlaven-
tana memory.
Permite seleccionar la velocidad de ejecuci6n paso a paso para la orden
Animate.
Esta orden permite seleccionar e1tipo deexpresiones que sevan aevaluar,
esto es, expresiones Basic, C0Fortran. Si activamos laopci6n Auto, el de-
purador reconoceni automaticamente enfunci6n del programa cargado el
tipo de expresi6n que tiene que evaluar.
Activa/desactiva e1intercambio depaginas devideo entre Code Viewy el
programa queseestadepurando (ver lasopciones IF y IS). Laopci6n swap
desactivada requiere menos memoria y hacequeel proceso seamas rapido
pero tiene una serie de limitaciones que no se dan cuando esta activa.
Indica aCode Viewsi enlarepetici6n delahistoria del proceso dedepura-
ci6n seutiliza el fichero .cvh, el fichero .cvi 0ambos (ver laorden History
On descrita anteriormente).
Esta orden solamente puede ser ejecutada si setiene un procesador 386.
Cuando laopci6n 386esta activada laventana register visualiza los regis-
tros enel formato 80386. Tambien permite ejecutar instrucciones querefe-
rencian registros de 32 bits. Si no esta activa cualquier dato almacenado
en la parte alta de un registro de 32bits seperdera. Esta opci6n no esta
disponible en modo protegido (CVP).
Esta orden presenta una lista denombres defunciones, ademas del modu-
lo principal, que han sido llamadas para su ejecucion y que aun no han
finalizado. Cada llamada esmostrada con los argumentos que sehan pa-
sado a la funcion.
A estalistadellamadas enocasiones seIedael nombre depilaporque
el nombre queesta enlaparte superior eslaultima 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 S3esteen
ejecucion, en lapila habra, dearriba aabajo: S3, S2, Sl, Ml. Cuando fi-
nalicelaejecucion deS3, enlapilaquedara S2, Sl, Ml y as!sucesivamente.
Supongamos que detenemos la ejecucion cuando seesta ejecutando
lafuncion S3y visualizamos el menu Calls. Si mediante lasteclas demovi-
miento del cursor nos situamos sobreel nombre Sl y pulsamos Enter, Code
Viewcolocael cursor sobrelasiguientesentenciaqueseriaejecutada cuando
el control sea devuelto a la funcion Sl y muestra en la ventana local las
variables correspondientes a esta funcion. Si en esteinstante pulsamos la
teclaF7la ejecucion continuara hasta estasentencia (sentencia sobrelaque
esta el cursor).
Comprime un fichero ejecutable reduciendo el espacio ocupado por lain-
formacion afiadida al fichero para hacer posible sudepuracion. EI fichero
comprimido puede ser cargado por Code View mas rapidamente.
Esta utili dad permite crear y modificar una base dedatos deayuda apar-
tir deuno 0 mas ficheros que contienen informacion formateada para el
sistema de ayuda.
Convierte programas enmodo 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 entiempo deejecuci6n 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 delacabecera deun fichero ejecutable.
EXEHDR genera un listado que muestra el contenido de la cabecera del
fichero einformaci6n a cerca de cada segmento en el fichero.
Borra todos los ficheros del subdirectorio oculto DELETED del directorio
actual 0deldirectorio 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 0Hamar 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
laeficiencia 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 0rutinas 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 enlenguaje ensamblador incorporadas enun programaC
pueden hacer queel programa no seaportable, por 10que seaconsejaque
estas rutinas seescriban como funciones 0como 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 quefor-
man el programa, vaprecedidapor lapalabra clave_asm. Lapalabra_asm
llama al ensamblador; puede aparecer en cualquier lugar valida parauna
sentenciaCeiraseguidapor unainstrucci6n enensamblador 0par ungrupo
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 enen-
samblador. Las instrucciones en ensamblador van encerradas entreHaves
y encabezadas por la palabra reservada _asm.
Una rutina enensamblador 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 enensamblador puede escribirse tambien delas 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
_asmpop
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:
SinHavesel compilador no puede distinguir donde finalizael cadi
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 serecomienda incluir el c6digo enlenguajeensam-
blador entre Haves, por las siguientes razones:
Las Havesseparan deuna forma clara el c6digo enensambladorde l
c6digo C.
Las Havesutilizadas enun 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 conjuntocom
pleto deinstrucciones correspondientes alos procesadores 80286y 80287
pero no reconoce las instrucciones que sean especificas delos procesado-
res 80386y 80387. Para utilizar instrucciones especificas delosprocesado-
res 80286y 80287 el programa C debe ser compilado con la opci6n/G 2.
Una constante entera puede ser expresada en las bases 2, 8, 10y 16afia-
dieildoel sufijo: B, Q 00, D yH respectivamente. Tambienpuedeemplearse
lanotacion C; por ejemplo las constantes l OOh y Oxl OO tienen el mismo
significado.
Aunque un bloque _asm puede referenciar objetos y tipos dedatos C, no
puede definir objetos para contener datos utilizando las directrices y ope-
radores del MASM. Especificamente, no sepueden utilizar las directrices:
DB, DW, DD, DQ, DT y DF 0los operadores DUP 0THIS. Tampoco es-
tandisponibleslasestructuras y registrosdel MASM; por 10quenosepueden
utilizar las directrices: STRUC 0RECORD. Sf son soportadas las directri-
ces EVEN y ALING.
Un bloque _asm en Microsoft C reconoce todas las expresiones soporta-
daspor el Macro Ensamblador deMicrosoft MASM, estoes, cualquier com-
binacion deoperandos y operadores que den como resultado un unico va-
lor 0unadireccion. Tambienreconocetodos losoperadores conlassiguientes
excepciones 0diferencias:
Lossegmentos no pueden ser nombrados. Utilizar explicitamente un regis-
tro; por ejemplo ES:[BX]. Si seutiliza el operador PTR, debeaparecer an-
tes de modificar el segmento.
Si seutiliza el mismo identificador para un miembro enmas deuna estruc-
tura0union, el nombre delaestructura 0union deben especificarse inme-
diatamente antes del operador " . ".
No es soportado en union con el operador DUP pero puede ser utilizado
para devolver e1numero deelementos deun 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 deuna variable. El tamafio deuna variable esel
producto de los valores devueltos por los operadores LENGHT y TYPE.
Este operador puede ser utilizado enun bloque _asm para devolver el ta-
mafio deun tipo 0deuna variable C. Si lavariable esun 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); 10que 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 16bits */
# define cwde _asm _emit Ox66_asm _emit Ox98
El c6digo maquina equivalente aCWDE es9866(hexadecimal); estos
bytes son introducidos por la macro cwde(primero el de menor peso) en
el segmento de c6digo como si de lapropia instrucci6n setratara. Por 10
tanto el procesador interpretara lamacro cwdecomo lainstrucci6n CWDE
del 80386.
Como hemos visto, un bloque _asm puede insertarse enun bloque C. Lo-
gicamente, las instrucciones del bloque _asm, igual que las sentencias de
C, pueden referirse alas variables C y utilizar otros elementos deC. Tales
elementos son los siguientes:
Constantes, incluyendo constantes simb6licas y miembros de una
enumeraci6n (enum)
Nombres detipos declarados por typedeJ, generalmente utilizados
conoperadores talescomo PTR y TYPE 0para especificar losmiem-
bros de una estructura 0uni6n.
Entendiendo por simbolo C un nombre dev<;lriable,etiqueta 0un nom-
bre de una funci6n (no constantes simb61icas 0miembros enum) las siguien-
tes reglas son aplicables':
1. Solamente un simbolo C puede ser referenciado par una instruc-
ci6n en lenguaje ensamblador. Dos 0mas simbolos no pueden apa-
recer en la misma instrucci6n en ensamblador a menos que se uti-
licen con los operadores TYPE, LENGTH 0SIZE.
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, unsigne d char x);
unsigne d 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, unsigne d 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 delamisma forma en C que enMASM
(*, I],... )tienen el significado propio del contexto en el que seutilicen.
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 enC debeseI:escritasobreunaunicalinea16gica.Por ello, cuan-
do lamacro ocupe mas deuna linea fisica, utilizaremos como canicter de
continuaci6n labarra 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 esteejemplo las Havespara delimitar el bloque _asm que forma
lamacro sonesenciales. Deotro modo el compilador asume lasinstruccio-
nessobrelamisma linea, ala derecha delamacro, como c6digo ensambla-
dor adicional.
En general, cuando un bloque _asm comienza aejecutarse 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 alos registros DI ySl
con algunas excepciones (por ejemplo, C utiliza estos registros cuando de-
finimos variables tipo register).Sinembargo, si hay quesalvar 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 esnecesa-
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), ano 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 esdetipo long, C asume los registros DX yAX, colocando eneste
caso lapalabra demayor peso, del resultado, enel registro DX ylapalabra
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 ne ar 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
/G r), e1compilador C almacena los argumentos pasados en registros en
lugar deenlapila. Esto puede traer problemas yaque no sabemos quere-
gistros sehan utilizado. Por ejemplo, si lafunci6n recibeun panimetro en
BX einmediatamente nosotros almacenamos un valor en BX, el panime-
tro se ha perdido.
Calcular laraiz cuadrada entera deun numero entero, mediante el si-
guiente algoritmo: "la raiz cuadrada entera deun numero entero n esigual
al numero deenteros impares (1, 3, ...) que pueden restarse sucesivamente
de dicho numero".
Comprobar que el numero de enteros impares buscado se correspon-
decon 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 e ntonce s 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 0perteneciente alas librerias estan-
dar, puede ser llamada desde un bloque _asm utilizando la instrucci6n
cal l . 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 proceder 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 deuna funci6n C puede ser reemplazado por un bloque _asm.
Esto esuti! cuando enocasiones necesitamos una mayor velocidad deeje-
cuci6n.
El siguiente ejemplo inicializa todos los elementos de un array aun
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( ) 0int86x( ) como veremos en el capitulo si-
guiente. No obstante, como yahemos vistoenalgunos ejemplos enestemis-
mo capitulo, es muy facil Hamar a una rutina de interrupci6n desde un
bloque _asm utilizando la instrucci6n into
El ejemplo quesemuestra acontinuaci6n indicadeuna forma sencillac6mo
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
-se gme nt 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 leeunacadena, laalmacena enun segmento denomina-
do segmentol, lainvierte, utilizando paraellolafunci6n invertir( ) yescri-
be el resultado obtenido.
Observar en lafunci6n main( ) c6mo seobtiene el valor del registro
DS utilizando un bloque _asm. Lafunci6n invertir( ) haside disefia'da
paradevolver un puntero far (segun el convenio C, en DX:AX). Observar
tambien queun puntero based ocupadosbytes; cuando espasado alafun-
ci6n 0cuando esdevuelto por lafunci6n, estratado como un puntero far;
10 que indica que tanto el segmento como el desplazamiento de ladirec-
ci6n de memoria son pasados y retornados.
Un miembro deunaestructura 0 deunauni6n puede referenciarse dentro
deun bloque _asm, utilizando el nombre delaestructura yel nombre del
miembro (ademas del operador punto) 0utilizando solamente el nombre
del miembro. Por ejemplo:
mov bx, offset estr
mov ax, [bx].miembro
Si el nombre del miembro aparece enmasdeunaestructura, esnece-
sario especificar el nombre delaestructura paraevitar ambiguedades. Por
ejemplo:
mov bx, offset estr
mov ax, estr[bx}.miembro
El ejempl0 siguiente muestra como acceder desde un bloque _asm,
a10smiembros de una estructura definida en C. Concretamente, esteejemplo
define dos estructuras: s1 y s2 asigna valores alaestructura 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 sehan definido estaticas (almacenadas enel segmento de datos,
DS). Si hubieran sido declaradas automaticas (aulo), estarian almacena-
dasen lapila; esto exigiriatrabajar 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 enlugar deestructuras definimos punteros aestructuras, el bloquc
_asm no cambia.
Las instrucciones debifurcaci6n enensamblador y en C pueden utilizarse
para saltar auna etiqueta dentro deun bloque _asm 0fuera deel. Cuan-
do sedefine una etiqueta enun bloque _asm no hay diferencia entre utili-
zar mayusculas 0minusculas. En cambio, cuando sedefine 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 Viewen el capitulo
anterior).
Lapresencia deun bloque _asm afecta alaoptimizaci6n 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-
cenalasvariables enlos registros. Cuando una funci6n contiene un
bloque _asm esto no ocurre ano ser que seindique explicitamente
con la palabra claveregister.
Finalmente, cuando una funci6n contiene un bloque _asm, quedan
inhibidas en toda la funci6n las siguientes optimizaciones:
Para poder enlazar un procedimiento escrito enlenguaje ensamblador con
un programa C debenutilizarse segmentos compatibles. Lossiguientespun-
tos pueden ser de gran ayuda:
2. Utilizar lasdirectrices .CODE y .DATA para declarar los segmen-
tos de c6digo y de datos respectivamente. (Con las versiones 5.0
o posteriores del macroensamblador deMicrosoft utilizar el for-
mato simplificado).
3. EI nombre del procedimiento debe ser declarado con la directriz
PUBLIC. Tambiendebeser declarado PUBLIC cualquier otro data
que se quiera utilizar desde otro modulo.
4. Los procedimientos ylos datos accedidos per larutina enensam-
blador deben ser declarados EXTERN. La forma mas segurade
utilizar EXTERN escolocar ladirectriz fuera decualquier defini-
cion de segmento.
push bp ;salvar bp
mov bp, sp ;preparar para acceder a pardmetros
El registro BP esutilizado para acceder alosparametros yalosdatos
locales almacenados en la pila (stack). EI registro SP no puede utilizarse
para esteproposito porque no esun registro indice 0base. Por otra parte,
SP cambia cuando semeten mas datos enlapila, mientras que el registro
BP permanece constante; asi que cada parametro puede ser direccionado
con un desplazamiento con respecto a BP.
EI valor deBP es salvado porque esnecesario cuando termine laeje-
cucion del procedimiento para poder continuar con la ejecucion del
programa.
Un procedimiento llamado debe salvar los valores delos 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 desde el program a pone en la pila cada 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) 0de 4bytes (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 sepone 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 (0direccion del segmento) en DX
Parte baja (0direccion 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 aun 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 desde C, si el modulo
es compilado bajo uno de los modelos small 0compact; declarar-
10FAR, 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
compartido 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 denumerosas rutinas quenosotros podemos utilizar acti-
vando lainterrupci6n correspondiente. Detodas ellas, lainterrupci6n 21H
tiene un especial interes, yaque nos permite acceder atodos los servicios
del DOS. (Para sacar provecho aestecapitulo debe conocerse laarquitec-
tura del microprocesador 8086y las interrupciones y servicios de DOS).
C tambien dispone defunciones quenos van apermitir realizar lain-
terrupci6n requerida, yejecutar asi larutina deseada del DOS. Las funcio-
nes que seestudian en estecapitulo utilizan las uniones y estructuras que
sepresentan 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 sedetaIl an a continuacion estan
contenidas en el fichero dos.h.
Esta funcion esutilizada para realizar una interrupcion directamenteal DOS
y ejecutar la rutina correspondiente.
Lafuncion int86( ) ejecuta lainterrupcion especificada por n_int. An-
tes deejecutarse lainterrupcion, int86( ) copia el contenido dein_regs en
10sregistroscorrespondientes. Despues deejecutarselaitlterrupcion, 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 sulugar utilizar lafunci6n int86x( ) 0lafunci6n intdosx( ).
EI valor devuelto por esta funci6n esel valor del registro AX despues
delainterrupci6n. Si el valor del campo cflag esdistinto de0, qui eredecir
queha ocurrido un error ylavariable _doserrno espuesta al valor corres-
pondiente.
Esta funci6n esutilizada para realizar una interrupci6n directamente al DOS
yejecutar una rutina querecibeun argumento enel registro ES, 0quereci-
be en el registro DS un valor diferente del segmento dedatos 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 deejecutarse lainterrupci6n, int86x( ) copia el contenido dein_regs
y de regs---.Segen los registros correspondientes. De regs---.Seg,solamente
son utilizados los valores delos registros DS yES. Despues de ejecutarse
la interrupci6n, int86x( ) copia los valores actuales de los registros en
out_regs ylos valores actuales deDS yES enregs---.Seg,restaurando DS.
Tambien copia el registro de flags en el campo cflag de out_regs.
EI valor devuelto por esta funci6n esel valor del registro AX despues
delainterrupci6n. Si el valor del campo cflag esdistinto de0, qui eredecir
queha ocurrido un error ylavariable _doserrno espuesta al valor corres-
pondiente.
Esta funci6n esutilizada para realizar una Hamada al DOS yejecutar una
rutina que reciba argumentos enotros registros distintos deDX (DH/DL)
y AL, 0para 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( ) 0la 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, 0que 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........seg en 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 esel valor del registro AX despues
delainterrupci6n. Si el valor del campo c/lag esdistinto de0, quiere decir
quehaocurrido un error ylavariable _doserrno espuesta al valor corres-
pondiente.
Esta funci6n copiaenuna estructura detipo SREGS apuntada por regs_seg
el contenido de los registros de segmentos.
Estafunci6n llamaal DOSparaejecutar unarutina especificadapar fo_dos
despues decopiar los contenidos dedos_dx ydos_a) enlos registros DX
yAL respectivamente. Para efectuar lallamada estafunci6n ejecuta lains-
trucci6n INT 21D.
La funci6n bdos() seutiliza para realizar llamadas al DOS yejecutar
rutinas que no tomen argumentos 0que solamente los tomen enlos regis-
tros DX (DH,DL) y/o AL.
No utilizar la funci6n bdos( ) para interrupciones que modifiquen el
registro DS. En sulugar utilizar lafunci6n int86x( ) 0lafunci6n intdosx( ).
El valor devuelto por esta funci6n esel valor del registro AX despues
de la llamada.
Esta macro es utilizada para copiar 0recuperar el offset de la direcci6n
far especificada por puotero.
Esta macro es utilizada para copiar 0recuperar 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 2de fa interrupcion 14d
/ *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 14a 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 14a 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) I I !(out~egs.h.ah & Ox80) I I
!(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 ay desde105
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 16bits indicando 10 que hay ins-
talado.
Leeel siguiente canicter desde el buffer de entrada del
teclado. Si no hay un canicter, espera por el.
Informa si hay algun canicter enel buffer deentrada del
teclado. EI canicter sequeda en el buffer hasta que se
lea con el servicio O.
Comunica los bits deestado del teclado, que indican el
estado de las teclas de tipo shift (si seha 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
bytede mayor peso el c6digo del teclado (scan code). Si el byte demenor
peso es 0 latecla pulsada corresponde auna tecla para el movimiento del
cursor 0 auna tecla defunci6n. Si el servicio es 1retorna un 0 si el buffer
estavacio, si no retorna el siguiente caracter en el buffer. Si el servicio es
2retorna el estado de las teclas tipo shift cuando estan activadas (1 Shift
derecho, 2Shift izquierdo, 4 Ctrl, 8 Alt, 16Scroll Lock, 32Num Lock,
64 Caps Lock, 128Ins).
Permite conocer lacantidad dememoria que tiene el ordenador. Este ser-
vicio se activa con la interrupci6n 18(INT OxI2).
Retorna el maximo numero debloques contiguos de memoria de lK
presentes.
unsigned _bios_printer(unsigned servicio, unsigned impresora,
unsigned dato);
Manda un unico octeto alaimpresora el cual secorres-
ponde con el byte de menor peso de dato.
Informa del estado delaimpresora. EI argumento dato
es ignorado.
EI byte de menor peso del valor retornado conti ene, para todos los
servicios, el estado delaimpresora (1:impresora apagada, 8: error deE/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);
Esteejemplo inicializael puerto 1conlossiguientesvalores: 2400bau-
dios, dato de 7 bits, 2 bits de stop y no paridad.
Esta funci6n retorna un entero. El bytedemayor peso, contiene infor-
maci6n de estado y el byte de menor peso depende del servicio.
Cada 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 (sehan traspasado los limites)
dato listo
Si el servicio es 1, el bit 15seria puesto a 1si el dato no pudiera ser
enviado.
Si el servicio es 2, el dato leido seria devuelto en el iJ ytede 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 03, en el byte de menor peso seda 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 (I NT Oxla).
Hay dos servicios: 0 para leer la hora y 1para 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 ( ) yaque nos permite ejecutar cualquier fichero
.COM, .EXE 0 .BAT,con 10 que tenemos acceso desdeun pragrama C a
cualquier orden del DOS.
Cuando seda formate aun disco desdeel DOS, secrea un directorio que
recibeel nombre dedirectorio raiz ( \ ). Las entradas enestedirectorio pue-
den ser ficheras 0bien otros directorios (llamados subdirectorios). Estos
ultimos, asuvez, pueden contener ficheros 0 bien subdirectorios, yasi su-
cesivamente.
Es una secuencia deuno 0 mas nombres dedirectorios, 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. Seasume 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 llevahasta el directorio rafz.
Significaquesepuede enviar lainformacion producida por una orden cual-
quiera, no solamente alasalida estandar, sino aun 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 estaorden grabamos lainformacion suministrada por DIR, acon-
tinuacion de la ya existente en INDICE.
Es posible obtener lainformacion deentrada para una orden no solamen-
te del teclado, sino de un fichero cualquiera utilizando el sfmbolo <en
la linea de ordenes. Por ejemplo:
Con estaorden clasificamos los datos del fichero ALUMNOS yel 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.
Sedenomina prompt al indicativo que sevisualiza en pantalla cuando se
tiene cargado el sistema operativo. Por defecto, el prompt esta formada
por el nombre delaunidad dedisco enlaqueestamos 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, seescribe el nombre de la unidad ala
que queremos ir seguida de dos puntos y se puisa Enter. Por ejemplo:
Ademas desaber enqueunidad estamos, esimportante saber tambien
enquedirectorio deesaunidad nos encontramos trabajando. Laordendel
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 enel
directorio EJ EMPLOS delaunidad C, el prompt que sevisualiza enpan-
talla es:
Observar el prompt de estos ejemplos (directorio en el que estamos)
para comprender la orden que se quiere ejecutar.
Cuando sequi erepasar un nombre de fichero (path-name) a un proceso
en una Hamada exec( ) 0en una funci6n, como par ejemplo la funci6n
system(), sedebeutilizar como delimitador una doblebarra 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 Ienlugar de \
queutiliza.MS-DOS. No obstante, MS-DOS reconoce como delimitador /
enaquellos lugares donde seespera un path. En un lugar donde no eses-
perado hay que utilizar \ \ .
Ellenguaie C dispone delasfunciones queacontinuaci6n seexponen para
control de directorios:
#include <direct.h >
#include <errno.h >
Lafunci6n chdir( ) devuelveel valor 0si seejecuta satisfactoriamente
y un valor -1si ocurre un error.
Este ejemplo, suponiendo queestamos enlaunidad B, conduce hasta
el directorio PROGINC delaunidad dedisco 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 >
Lafunci6n mkdir( ) devuelveel valor 0si seejecuta satisfactoriamen-
tey un valor -1si 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 >
Lafuncion rmdir( ) devuelveel valor 0si seejecuta 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 lalongi-
tud maxima para el nombre del camino. Si la longitud maxima deestees
superior a n se obtiene un error.
#include <direct.h >
#include <errno.h >
La fundon getcwd( ) devuelveun 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 .exedel 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
secontinua en la siguiente sentencia del programa. El argumento cadena
puede ser un programa .EXE, .COM 0.BAT.Si el valor decadena esNULL
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 ( ) devuelveun valor distinto decero si cadena vale
NULL y COMMAND.COM existey un valor 0 si COMMAND.COM no
existe.
Si cadena tiene un valor distinto de NULL la funci6n system( ) de-
vuelveel valor 0si seejecuta satisfactoriamente y un valor -1si ocurre un
error.
Cuando seejecuta esta funci6n secarga y ejecuta una nuevacopiade
COMMAND.COM, por 10quesystem ( ) 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 enviael resultado
al fichero alum, copia alum en alumnos y borra alum. Ahora setiene el
fichero alumnos clasificado, que sevisualiza en pantalla.
Esta linea da lugar a que secreeel fichero INDICE con el contenido
clasificado del directorio.
Estafunci6n determina si el fichero especificado por path existeysi 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 existe0 si no esaccesibleenel modo especifi-
cado, lafunci6n devuelveun valor -1 y lavariable errno espuesta al valor
correspondiente.
Esta funci6n cambia el permiso que tiene el fichero especificado por path
por el especificado por pmodo. El significado deesteargumento esel 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( ) devuelveun 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 0trunca ala longitud especificada por n el fichero
cuyo numero asociado esnum. El fichero debeser abierto enun modo que
permita escribir. Si e1fichero esextendido seafiaden caracteresnulos (' \ 0').
#include <io.h >
#include <errno.h >
Lafunci6n chsize() devuelveun 0 si el fichero escambiado. 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( ) devuelveun 0 si el fichero esborrado. 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 0directorio 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 deun 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 ylaalmacenaenlaestructura detipo 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 devuelveun 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 estaasociado con un dispositivo (un termi-
nal, consola, impresora 0 puerto serie).
La funci6n isatty( ) devuelveun valor distinto de0 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 laque fue modificado por ultima
vezel fichero especificado por path. El fichero tiene quetener 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 10que este concepto es ignorado. Si
tesNULL la modificaci6n sehace 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 lavariable 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 pasado 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 desde 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 seexponen a continuaci6n seencuentran
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 16bytes y seg es la direcci6n del segmento donde
va aser ubicado el bloque. La funci6n retorna un valor 0 si seejecuta satis-
factoriamente. Un valor distinto de 0 indica un error.
Esta funci6n cierra el fichero que tenga como descriptor num utilizando
lafunci6n Ox3Edel DOS. Lafunci6n retorna un valor 0 si seejecuta satis-
factoriamente. Un valor distinto de 0 indica un error.
Crea un fichero utilizando la funci6n Ox3Cdel DOS. Si el fichero existe
seborra. jich esel 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 esunpuntero
auna variable quecontiene el descriptor del fichero. Verlafunci6n 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 dememoria asignado por __dos_allocmen() utilizand
lafunci6n Ox49del DOS. Lafunci6n retorna un valor 0 si seejecutasati
factoriamente. Un valor distinto de 0 indica un error.
Encuentra la primera entrada de directorio que concuerde con losdatos
especificados utilizando la funci6n Ox4E del DOS. injo-fich esunaes-
tructura detipo find_t declarado endos.h, que contiene informacionsa-
breel fichero. Lafunci6n retorna un valor 0 si seejecuta satisfactoriamen
te. Un valor distinto de 0 indica un error.
Encuentra la siguiente entrada de directorio que concuerde utilizandola
funci6n Ox4F del DOS.
Obtiene lafecha utilizando lafuncion Ox2A del DOS, fecha esuna estruc-
tura detipo dosdate_t declarado en dos.h, que contiene los datos relati-
vos a la fecha.
Obtiene la unidad dedisco actual (A: = 0, B:
Ox19 del DOS.
Obtiene el espacio libredel disco utilizando la funcion Ox36 del DOS, drv
eslaunidad dediscoy esp esunpuntero auna estructura detipo 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 seejecuta satisfacto-
riamente. Un valor distinto de 0 indica un error.
Obtiene lafechay hora asociadas con el fichero utilizando lafuncion Ox~7
del DOS. La funcion retorna un valor 0 si seejecuta satisfactoriamente.
Un valor distinto de 0 indica un error.
Obtiene lahora utilizando lafuncion Ox2C del DOS, hora esuna estructu-
ra detipo dostime_t declarado en dos.h, que contiene los datos relativos
a la hora.
Obtiene el vector deinterrupcion asociado con el numero deinterrupcion
especificado utilizando la funcion Ox35 del DOS.
Instala un programa residenteenmemoria (TSR) utilizando lafuncion Ox31
del DOS, cod esel codigo desalida y t esel numero debloques de 16bytes
necesariosparainstalar el programa. El espacioenexcesoesdevueltoal DOS.
Abreun fichero utilizando lafuncion Ox3Ddel DOS. Verlafuncion 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);
Leedatos de un fichero utilizando la funcion Ox3Fdel DOS. Ver la fun-
cion read( ). num es el descriptor del fichero, buffer almacena los bytes
leidos, cesel numero debytes aleer yb esel numero debytes actualmente
!eidos. La funci6n retorna un valor 0 si seejecuta satisfactoriamente. Un
valor distinto de 0 indica un error.
Cambia el tamafio de un bloque de memoria, previamente asignado por
_dos_allocmen, utilizando lafuncion Ox4Adel DOS, t esel nuevo tama-
fio, seg esdireccion del segmento del bloque dememoria ytm esun punte-
ro a un entero que indica el tamafio maximo que puede ser asignado. La
funcion retorna un valor 0 si seejecuta satisfactoriamente. Un valor distin-
to de 0 indica un error.
Establece la fecha utilizando la funcion Ox2Bdel DOS, fecha es una es-
tructura detipo dosdate_t declarado en dos.h, que contiene los datos re-
lativos a la fecha.
Selecciona launidad de disco utilizando la funcion OxOEdel DOS, drv es
O=unidad actual, 1=A:, 2=B:, etc.; num_drv esun puntero aun entero
que indica el numero total de unidades.
unsigned _dos_setfiJ eattr(const char *path, unsigned atrib);
:~tablece los atrib.~tos del fichero 0directorio utilizando la funcion Ox43
U
eDOl S.d~a .funclOnretorna un valor 0 si seejecuta satisfactoriamente
n va or lstmto de0 indica un error. .
unsigned _dos-setftime(int num, unsigned fecha, unsigned hora);
Establecelafechayhora asociadas conel fichero utilizando lafuncion Ox57
del DOS. La funcion retorna un valor 0 si seejecuta satisfactoriamente.
Un valor distinto de 0 indica un error.
Establece lahora utilizando lafuncion Ox2Ddel DOS. hora esuna estruc-
tura detipo 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 deinterrupcion (pint) para un numero deinterrupcion
(num_int) especificado, utilizando la funcion Ox25del DOS.
unsigned _dos_write(int num, void _far *buffer,
unsigned c, unsigned *b);
Escribe datos enun fichero utilizando lafuncion Ox40del DOS. Verlafun-
cion writer ). num es el descriptor del fichero, buffer contiene los bytes a
escribir, c esel numero de bytes aescribir y b esun puntero al numero de
bytes actualmente escritos. La funcion retorna un valor 0 si seejecuta sa-
tisfactoriamente. Un valor distinto de 0 indica un error.
Obtiene informacion extendida deun error utilizando lafuncion Ox59del
DOS. info_error esun puntero auna estructura detipo 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 amedida (pint) por laque proporciona el DOS. El retor-
no desde esta rutina puede producirse desde una sentencia return, desde
una funci6n Jardresume( ) 0desde una funci6n _hardretn( ).
Retorna al DOS despues de un error de hardware. resu puede tener uno
delos siguientes valores: _HARDERR_IGNORE, _HARDERR_RE-
TRY, _HARDERR~BORT, _HARDERR_FAIL.
Retorna al programa despues deun error dehardware. error esun c6digo
de error de DOS.
Segunhemos visto lafunci6n Ox31 delainterrupci6n Ox21 (0 sueqpivalen-
te INT Ox27) proporcionan el mecanismo para dejar residente un progra-
maenmemoria (TSR). Normalmente un programa residente consta dedos
partes: laparte residente que queda enmemoria y laparte 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 esteejemplo utilizaremos ademas delafunci6n Ox31, la
interrupci6n OxIC ("tic" del temporizador). La interrupci6n de software
OxIC es invocada por la interrupci6n Ox08 cada vez que esta seproduce.
El temporizador del sistema emite la interrupcion Ox08cada 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 10expuesto, una rutina del usuario apuntada por el vector de
interrupcion correspondiente al numero de interrupcion OxIC se ejecutara
por cada "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 OxICsea reemplazado 0hasta que se
reinicialice e1sistema.
Mientras mi_int no esinvocada el cursor queda libre (no seesta es-
cribiendo el re1oj), 10quepermite disponer del prompt del DOS y ejecutar
cualquier otro proceso.
Cada programaqueesejecutado por el sistema operativo debeser primero
transformado enun proceso. Un proceso esuna unidad ejecutable, alaque
el sistema operativo Ieha 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 esun sistema operativo monotarea; esto significa que
s610 es posible ejecutar un proceso cada vez. Esta circunstancia no seda
en un sistema operativo multitarea como OS/2 0 como UNIX.
En el DOS, lainiciaci6n, ejecuci6n y finalizaci6n deunproceso esres-
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 einiciar otro proceso hijo. Las funciones que vamos
a exponer en estecapitulo tienen como finalidad la iniciaci6n, ejecuci6n
y finalizaci6n de un proceso.
La iniciaci6n de un proceso bajo DOS, llevaconsigo 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 seaejecu-
tado. La distribuci6n de este espacio es de la forma siguiente:
2. Seconstruye el prefijo del segmento del programa (PSP). Estetiene
un tamafio de 256bytes 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 dein-
terrupci6n Ox22, Ox23 y Ox24 con el valor original que tenian
antes de comenzar la ejecuci6n del proceso.
Primera direcci6n segmentada por encima delamemoria asig-
nada al programa. Un programa puede utilizar esta direcci6n
para determinar el tamafio real delamemoria quetieneasignada.
Llamada al planificador de funciones del DOS. Su funci6n es
ejecutar larutina deservicio correspondiente al numero defun-
ci6n que Iees pasado.
La direcci6n a la que sepasani el control cuando la ejecuci6n
del programa finalice con una llamada alaINT Ox20. Este va-
lor secopia en el vector de interrupci6n Ox22.
Ladirecci6n alaquesepasani el control encaso deun Ctrl +C.
Este valor se copia en el vector de interrupci6n Ox23.
La direcci6n a laque sepasani el control encaso deun "error
fatal". Este valor secopia en el vector de interrupci6n Ox24.
20 octetos que contienen los descriptores (0 a 19)para manejo
delos ficheros abiertos. Los5primeros estan normalmente asig-
nados y secorresponden con los dispositivos estandar deE/S.
Direcci6n del bloque que contiene las variables deentorno. En
C estainformaci6n esgestionada por lafunci6n getenv( ) ypu-
tenv( ).
Parametros delalineade6rdenestecleadospor el usuario cuando
invoc6 al proceso (128bytes). En C argc y argv permiten pasar
estosparametros alafunci6n main(). Esteareasirveigualmente
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 deque el proceso termine normalmente 0 no, que el DOS
recupereel control sobrelosrecursosquehan sidoasignados adichoproceso.
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
estaclaro quehay quesalvar enlapila el estado delafunci6n ac-
tual (setjmp( )). Esto permitira mas tarde restaurar (longjmp( ))
el estado enel queseencontraba el sistemaantesdeefectuar el saIto.
SIGABRT
SIGFPE
SIGINT
Terminaci6n anormal
Excepciones en coma flotante
Ctrl +Break 0Ctrl +C
Las senales deproceso 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 deque 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 yantes dequeestedevuelvael 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( ) 0por la funci6n system( ).
Cuando un proceso finaliza de una forma normal son ejecutadas las si-
guientes tareas:
1. Seejecutan en orden inverso (LIFO) las funciones invocadas por
atexit( ) uonexit( ).
4. Secierran todos los ficheros abiertos y seborran todos los fiche-
ros temporales.
5. Seejecutan todos los procesos determinaci6n (coma flotante, res-
taurar divisi6n por cero (INT OxOO), restaurar interrupciones so-
lapadas, restaurar INT Ox22, Ox23y Ox24).
6. Setermina el proceso (INT Ox21,fund6n Ox4C): selibera lame-
moria asignada al proceso, excepto lamemoria asignada dimimi-
camente por e1propio proceso, y seretorna un c6digo de salida
al proceso padre.
Delas funciones disponibles para terminar un proceso, s610exit() fi-
naliza un proceso normalmente. Las fundones abort( ) y assert( ) s610in-
c1uyenel punta 5, terminando el proceso de una forma anormal.
Cuando seejecuta un programa desdeel sistemaoperativo seiniciaun pro-
ceso. Utilizando las funciones decontrol deprocesos sepuede inidar, eje-
cutar y parar un proceso, desde cualquier otro proceso.
Esta fund6n ejecuta una terminaci6n no normal de un proceso. No des-
carga losbuffers delos ficheros yno ejecuta los procesos lanzados por ate-
xit( ) uonexit( ).
#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 finalizadeuna forma normalla ejecudon deunproceso. Antes
dedar por finalizado el proceso lafundon exit( ) ejecuta enorden 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
salidadado por estado. Estafuncion espreferibleacualquier otraquepueda
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 dalugar aquelafundon referendada por fune seaejecutada
cuando el programa termina normalmente 0 por viaexit ( ). Pueden ser re-
gistradas unmaximo de32fundones paraser llamadas sucesivamente.Cuan-
do existen varias llamadas consecutivas a la fundon atexit( ) seejecutan
desde la ultima a la primera.
Esta fundon devuelveun 0 si seejecuta satisfactoriamente 0un 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 deesta, solamente puede utilizarse cuando fune esun puntero auna
funci6n de tipo onexit_t.
Es preferible utilizar la funcion atexit( ) yaque es una funcion C es-
tandar.
Una llamada a esta funcion salva en la variable eDt, el actual entorno de
lapila(conjunto devaloresqueseguardarian enlapila, por ejemplo, cuando
sellama 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( ) devuelveun valor 0 despues desalvar el entorno
delapila; 0bien, devuelveel argumento valor delongjmp() si acontinua-
cion desetjmp( ) sellamo aesta funcion (ver longjmp( ). Si el argumento
valor de longjmp( ) es 0 la funcion setjmp( ) retorna un 1.
Recordar que una sentencia debifurcacion incondicional goto puede
solamente transferir el control auna etiqueta dentro delapropia funcion.
Por 10 tanto, para ejecutar un salta deuna funcion aotra (no local) utilizar
conjuntamente las funciones setjmp( ) y longjmp( ).
Restaura el entorno (eDt) salvado por setjmp( ) y devuelve el control ala
sentencia que esta a continuacion de setjmp( ).
EI argumento valor es un valor distinto de 0 para devolverselo a
setjmp( ).
Losvalores delasvariables register delarutina quellam6 alafunci6n
setjmp( ) no son restaurados despues deejecutarse la funci6n longjmp().
EI resto de las variables conservan sus valores.
Esta funci6n reinicializa el paquete matematico para operar en coma flo-
tante. Normalmente seutiliza 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 aun proceso escoger una devarias formas demani-
pular una serral deinterrupcion. Lafundon signal( ) indica que seejecute
la funci6n June cuando durante laejecuci6n del proceso sedelainterrup-
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. Terminael proceso. C6digo de
salida 3.
La acci6n que setoma cuando serecibe la seftal de interrupci6n de-
pende del valor deJune. EI argumento June debe ser la direcci6n deuna
funci6n 0 una constante de las siguientes (definidas en signal.h):
Respuesta por defecto del sistema (equivalente alalla-
mada a la funci6n abort( ).
Ignorar laseftal deinterrupci6n. No utilizarla conSIGF-
PE ya que el estado del proceso queda indefinido.
Este ejemplo indica que si seproduce un error en coma flotante, se
ejecutani la funci6n del usuario mi---fune( ).
Si el argumento June secorresponde con ladirecci6n deuna funci6n,
entonces dicha funci6n seinstala como rutina demanipulaci6n para lase-
nal dada. Si la funci6n realiza un return, el proceso reanuda la ejecuci6n
inmediatamente acontinuaci6n del punta enel queserecibi6lainterrupci6n.
Todas las funciones June demanipulaci6n de senales tienen un argu-
mento (sig) yno devuelven un resultado (void), excepto SIGFPE que utili-
zaun argumento adicional, subeod, queidentifica el tipo deerror. Lascons-
tantes validas (FPE-'CXX) para esteargumento estan definidas en float.h.
Por ejemplo: FPE---ZERODIVIDE, FPE_OVERFLOW, FPE_SQRT-
NEG, etc.
EI valor deJune no es restaurado a su valor despues derecibir la se-
nal. Para recuperarse de un error de coma flotante tenemos que utilizar
conjuntamente las funciones setjmp() ylongjmp(). Con respecto aSIGF-
PE, si la funci6n instalada realiza un return, el proceso sereanudaria con
un estado indefinido.
Como las rutinas de manipulaci6n de senales son llamadas asincro-
namente cuando ocurre una interrupci6n, es posible que nuestra funci6n
demanipulaci6n coja el control cuando una operaci6n C seesteejecutan-
do yaun no haya terminado (estado desconocido). Por ello, aestetipo de
rutinas 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 0indirectamente la me-
moria para asignaci6n dinamica (por ejemplo mal/oe(), strdup( ),
etc.).
3. No incluir funciones C quegeneren llamadas al sistema(por ejem-
plo getewd( ), timer ), etc.).
4. No utilizar lafunci6n longjmp( ) ano ser que lainterrupci6n sea
originada por un error encoma flotante (per ejemplo el argumento
sig es SIGFPE).
Unaselial puestapor signal() no esheredada por unprocesohijocreado
por medio de exec( ) 0spawn( ).
Esta funci6n envia la selial sigal proceso activo (programa). Si anterior-
mente seha instalado una rutina demanipulaci6n (funci6n signal( )), en-
tonces lafunci6n raise() hace queseejecute dicha rutina. En caso contra-
rio se ejecutani la acci6n por defecto.
Lafunci6n raiser) devuelveun 0 si seejecuta 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 enmemoria por el proceso hijo, el cual esejecutado 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
sepasan y c6mo sepasan. A continuaci6n seindican 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 elementodel arrayenvp, exceptoel elemento final, apun-
ta a una cadena de la forma:
donde VAR esel nombre delavariable y valor es lacadena
de caracteres asociada.
char *mi_en vp[ J
{
"VARl=abcde':
"VAR2=xxx':
NULL
};
Lafunci6n exec() devuelveun valor -1 si ocurre un error ylavariable
errno es puesta al valor correspondiente.
Los ficheros que estan abiertos cuando se ejecuta una Hamada a la
funci6n exec( ) permanecen abiertos ene1nuevoproceso. Lafunci6n exec( )
no conserva el modo detraslaci6n delos ficheros abiertos; si es necesario
utilizar lafunci6n setmode( ). EI proceso hijo hereda el cntorno del proce-
sopadre. Esteentorno puede ser modificado por medio delavariable 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 1y 8(0 para sa/ir): ");
car = getche();
if (car- == '0') exit(O);
}
while ((car <'0') I I (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 creay ejecuta un nuevo proceso hijo. Ladiferencia conexec( )
viene dada par el argumento modo; estedetermina la acci6n que toma el
proceso padre, antes deydurante laejecuci6n del proceso hijo. Lossufijos
expuestos para exec( ) son v<ilidostambien 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 quelas llamadas efectuactas
con la funcion exec( ) (destruye el proceso padre).
Suspende el proceso padre hasta quefinalicelaeje-
cucion del proceso hijo (spawn sincrono).
Continua laejecucion del proceso padre concurren-
temente con el proceso hijo (spawn asincrono; va-
lido solamente en modo protegido).
Continua la ejecucion del proceso padre eignora
las llamadas a wait( ) 0cwait( ) contra los proce-
sos hijo (spawn asincrono; valida solamente en
modo protegido).
Continua laejecucion del proceso padreconcurren-
temente con el proceso hijo. El proceso hijo sedes-
ligadelajerarquia deprocesos padre; si el proceso
padre termina, el proceso hijo continua ejecutan-
dose (spawn asincrono; valida solamente enmodo
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) esel 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( ) 0cwait( ) 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 esta 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( ) 0closer ).
# 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') I I (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 devuelveun puntero alaentrada delatabla que contie-
ne la variable 0un valor NULL si la variable no esta definida.
Esta funci6n anade, borra 0modifica una variable delalista devariables
del entorno.
Esta fundon devuelveun 0 si seejecuta 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) 0HGC (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 16colores), tiene una resolu-
ci6n de 640 x 480, 10que 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 volveral modo original ambos todos
_TEXTBW40 40 cols. texto, 16grises texto CGA
_TEXTC40 40 cols. texto, 16/8 colores texto CGA
_TEXTBW80 80 coIs. texto, 16grises 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, 16eolores gnifieos EGA
_HRESI6COLOR 640 x 200 pixels, 16eolores grafieos EGA
_ERESNOCOLOR 640 x 350 pixels, BW gnifieos EGA
_ERESCOLOR 640 x 350 pixels, 4 0 16eolores gnificos EGA
_ VRES2COLOR 640 x 480 pixels, BW gnifieos VGA
_ VRES16COLOR 640 x 480 pixels, 16eolores gnifieos VGA
_MRES256COLOR 320 x 200 pixels, 256 eolores grafieos VGA
_ORESCOLOR 640 x 400 pixels, 1 0 16eolores (Olivetti) grafs.
Para utilizar el modo _HERCMONO hay que instalar previamente
el programa residente MSHERC.COM, suministrado con el paquete Mi-
crosoft C.
Yaquealgunos adaptadores gnificos soportan varios modos devideo,
Microsoft C provee dos modos mas:
Selecciona el modo devideo demas alta resolu-
ci6n de entre todos los soportados por nuestro
hardware.
_MAXCOLORMODE Seleccionael modo devideo quesoporte mas co-
lores de entre todos los soportados por nuestro
hardware.
Para seleccionar una modalidad devideo delas 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 enla
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 acontinuaci6n 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 aotras configuraciones (CGA, EGA
o VGA). Esta operaci6n serealiza 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) I I (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: %s\n':
nombres[iJ);
c +=
sprintj(b
+
c, "Puntos eje X' O/Od\n':
cv.numxpixels};
c +=
sprintj(b
+
c, "Puntos eje }; O/Od\n':
cv.numypixels};
c += sprintf(b
+
c, "Columnas de texto: O/Od\n':
cv.numtextcols};
c +=
sprintj(b
+
c, "Fi/as de texto: O/Od\n':
cv.numtextrows};
c +=
sprintj(b
+
c, "Colores: O/Od\n':
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/Od\n':
cv.mode};
c += sprintf(b
+
c, ''Adaptador: O/Od\n':
cv.adapter};
c += sprintf(b
+
c, "Monitor: O/Od\n':
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 16colores 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 Verdeclaro _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 15d
/ * 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 a7) los 16colores
posibles en los que se puede presentar un texto.
Si al valor del color del primer plano Iesumamos 16, seobtienen ca-
racteres parpadeando; esto es, los valores 16a 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.
Existendos modalidades devideopara gnificos encolor utilizando unadap-
tador CGA: ~RES4COWR y ~RESNOCOWR. Con CGA enalta
resolucion,_HRESBW, solo es posible trabajar en blanco y negro.
En modo gnifico un pixel esrepresentado por un bit (blanco y negro),
dos bits (4colores), cuatro bits (16colores), u ocho bits (256colores), de-
pendiendo del modo seleccionado.
Con la modalidad devideo ~RES4COWR, sedispone decuatro
pal etas decuatro colores cada una. Cada color tiene asociado un valor or-
dinal de0 al 3. El color 0 esel color defondo, 10que producini una salida
invisible y los colores dell al 3, son tres colores de los 16posibles.
EI color de fondo puede tomar cualquier valor de0 a 15ypara selec-
cionarlo disponemos de la funci6n:
Verde
Rojo
Marr6n
Cyan
Magenta
Oris claro
Verdeclaro
Rojo claro
Amarillo
Cyan claro
Magenta claro
Blanco
Utilizando lamodalidad devideo ~RESNOCOLOR con un moni-
tor blanco ynegro seproducen distintas tonalidades degrises. Si seutiliza
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"Oa 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 (4bits): 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, cada valor de atributo design a un registro de
los 16 posibles de la paleta, cada 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, seutiliza 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 16paletas, 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 graJ icas necesitan informacion acerca delaposicion (coor-
denadas x,y) donde sequiere dibujar. Estas coordenadas sepueden expre-
sar de dos formas:
El sistema decoordenadas fisicas es el establecido por omision. Tiene su
origen ene1punta (0, 0). Losvalores enestesistema son siemprepositivos.
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 decoordenadas logicas escreado al mover el origen auna 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 10que el resto de las funciones gnificas referinin los valores de
coordenadas empleados, a estepunto. Los valores dex ey, 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 programa 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 decoordenadas 16gicasafisicas utilizar
lafunci6n ---I?etphyscoord(x, y). Losresultados son devueltos enlaestruc-
tura xycoord.
Para utilizar las funciones graficas pensemos primero si hemos incluido
lalibreria gnlfica enel modelo dememoria que estemos utilizando. Deno
ser asi, realizar el enlace utilizando explicitamente estalibreria. Las decla-
raciones para estasfunciones estan enel ficherograph.h. Laspodemos agru-
par en funci6n de la tarea que desempefian, asi:
_GCURSORON
_GCURSOROFF
cursor visible
cursor no visible.
Cuando seejecuta un programa grafico, el cursor, por defecto, esvisi-
ble en modo texto y no visible en modo grafico.
Selecciona lamodalidad devideo apropiada para lainterface devideo ins-
tal adaene1ordenador. El argumento modo esuna constante delasespeci-
ficadas en la tabla expuesta en este mismo capitulo.
Esta fund6n devuelveun valor distinto de0 si lamodalidad devideo
elegida es soportada, en caso contrario el valor devuelto es O.
Esta funci6n devuelveel numero defilas actualmente puestas. Un va-
lor 0 significa que el modo devideo no es soportado. Puede suceder que
e1valor Ii/as no sea soportado, 10 cual no significa un error.
Almacena enuna estructura detipo videoconfig los parametros delacon-
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 delaejecucion del programa. El argu-
mento pag selecciona lapagina activa enun 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 sedispone de 16K deRAM para soportar multi-
ples paginas devideo 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 lapagina devideo avisualizar. Mientras tan-
to el programa puede continuar eir 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 lapagina 0 6 1yvisualiza la 16 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 detipo 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 detipo 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 ala 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 10soportan. Los adaptadores EGA y VGA disponen delaca-
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 delos colores. El primer valor en
el array seria el nuevo color asociado con el color o.
La funci6n _remapallpalette( ) devuelve un valor distinto de0 si se
ejecuta satisfactoriamente y un 0 en caso contrario. La funci6n
_Jemappalette( ) devuelve el color anterior 0un -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 0un -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 grcificoel color de fonda debe especificarse par media de
la constante correspondiente.
Devuelvecomo resultado el color de fonda actual. Par defecto estevalor
es O.
Pone como color del primer plano el indicado par el argumento color. Par
defecto estevalor esel valor mas alto delapaleta can la que estemos tra-
bajando.
La funcion ---setcolor( ) devuelvecomo resultado el color previa aun
-1 si ocurre un error.
Devuelvecomo 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 delinea esfijado par una mascara de 16bits. Cada
bit representa un punta en la linea. Si un bit es 1 sedibuja un punta y si
es 0 no se dibuja. El argumento mascara es por defecto OxFFFF, 10que
da lugar a una linea continua.
Este ejemplo fija la mascara 1100110011001100, 10que da lugar a li-
neas discontinuas.
Devuelveun numero correspondiente al valor actual delamascara que se
esta utilizando para trazar lineas por otras funciones. La mascara es un
valor de 16bits donde un 1significa dibujar un punta yun 0 no dibujarlo.
En terminos mas tecnicos diriamos: si el bit es 1el punta correspondiente
sepone al color delalinea ysi el bit es0 el punta correspondiente sedeja
como esta, no cambia. Por defecto el valor de la mascara es OxFFFF.
Este ejemplo almacena en lavariable estilo el valor actual delamas-
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=[ H\xOO\x3F\x30\x30\x3C\x30\x30\x30" 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 serepresenta 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 lamascara actuales. Los argumentos
x ey corresponden alas coordenadas deun punto. Si el punto esta dentro
de la figura serecubre su interior y si esta fuera serecubre el exterior. El
punto no debeestar sobre el borde delafigura. El argumento limite esuna
expresi6n numerica que identifica el color utilizado para pintar el borde
de la figura. El area a recubrir queda limitada por este borde.
La fundon --floodjill( ) devuelveun valor distinto de0 si seejecuta
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
Muevelaposicion actual desalida gnifica al punta decoordenadas (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
seutiliza la funcion -floodfil/( ) para recubrir una figura el borde debe
ser una linea continua.
La fundon _lineto( ) devuelveun valor distinto de0 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 einferior derecha respectivamente. EI argumento
c es una de las constantes siguientes:
La funci6n _rectangle( ) devuelve un valor distinto de 0 si se ejecuta
satisfactoriamente 0un 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);
---set linestyle( 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 delaelipseesel centro del rectangulo defini-
do por los puntos (xl, yl) y (x2, y2). El argumento cesuna delas constan-
tes siguientes:
La funci6n _ellipse( ) devuelve un valor distinto de 0 si se ejecuta sa-
tisfactoriamente 0un 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 0un 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 suinterior un sector circular,
coloreando ambas figuras.
Lafuncion ~etpixel( ) devuelvelas 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 16gicasde la posicion actual en una estructura
detipo 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 15corresponden a los colores normales y los valores 16a
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 alaposicion actual del cursor
en el texto. Por defecto es el valor mas alto.
Situa el cursor enlafilay 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 esescrito apartir delaparte superior delaventana. Cuando
la ventana sellena se hace scroll automciticamente.
Controla si el texto cubre una nueva linea 0setrunca cuando sealcanza
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
vezdefinida laventana, las referencias hechas afilaycolumna para situar
el cursor semiden con respecto alos bordes delaventana. Tambh~nutiliza
la funcion _wrapon( ) con el fin dever el efecto que produce en sus dos
modalidades. Observar que no seha definido una modalidad degraficos,
por no ser necesario cuando setrabaja solamente con texto.
Almacena enel area dememoria apuntada por imagen, lafigura delapan-
talla encerrada enun rectangulo definido por lospuntos (xl, yl) y(x2, y2).
El area dememoria debe ser 10suficientemente 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);
Dacomo resultado el numero debytes necesarios para almacenar lafigura
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.
Lafund6n _imagesize( ) devue1veel numero debytes necesarios para
almacenar la figura.
buffer =(char *)malloc((unsigned int) _imagesize(O, 0, 80, 50));
if (buffer ==(char *)NULL) exit(-l);
Transfiere alapantalla lafigura almacenada enlazona dememoria apun-
tada 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 esopuesta a--f5etimage( ). Da lugar auna
copia exacta de la imagen almacenada.
es la misma que _GPSET, excepto que produce una
imagen negativa.
ejecuta laoperad6n AND entre laimagen almacenada
y la de la pantalla. Seutiliza 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 ame-
nudo para animaci6n.
La animacion de un objeto serealiza de acuerdo con la siguiente secuencia
de pasos:
3. Borrar la imagen de la pantalla (-putimage( )) y dibujarla en la
nueva posicion.
Una imagen seborra 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 dela misma.
Los siguientes ejemplos, muestran con claridad 10anteriormente ex-
puesto.
El siguiente ejemplo visualiza el resultado que se obtiene al desplazar
un bola a 10ancho de la pantalla, utilizando los cinco modos de accion
(PSET, PRESET, XOR, OR y AND). Observar que la animaci6n real se
produce cuando seejecuta la sentencia:
buffer almacena lamatriz depixelscorrespondientes alaimagen 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)
exi t(!----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, seutiliza
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 laimagen: -putimage() conlaopci6n _GXOR, enlamis-
ma 10calizaci6n del punta 1.
4. Volveral punto 1, para dibujar laimagen enlanuevalocalizaci6n
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 pe10taque 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 I I 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 quesimulelosmovimientos deuna 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 ydentro deunos limites. Estos valores, al represen-
tarlos utilizando toda lapantalla 0una ventana, necesitanin enlamayoria
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 0en su defecto sobre toda la pantalla. Los argu-
mentos (wxl, wyl), especifican laesquina superior izquierda delaventana
sobrelaqueseencuadra el sistema decoordenadas ylos argumentos (wx2,
wy2), especifican laesquina inferior derecha deestaventana. El origen de
coordenadas es el (0, 0). El argumento inver puede tomar como valores:
estableceel sistemadecoordenadas cartesianas haciendo que
"y" aumente de abajo a arriba de la pantalla.
estableceel sistemadecoordenadas 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 devuelveun valor distinto de0 si seejecuta 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, losvalores dentro delos limites establecidos, sere-
presentan aescalasobretoda lapantalla. Si quisieramos quelarepresenta-
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 sedefine un sistema decoordenadas 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 yahan sido comentadas anteriormente, pero pensan-
do enun sistema decoordenadas ffsico(coordenadas depantalla) 0 16gico
(el origen 10situamos sobre un punta cualquiera delapantalla). A conti-
nuaci6n las exponemos, pensando ahara enun sistemadecoardenadas 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, doublewyl, doublewx2, double
wy2, char _far *imagen);
void_far _getimage_wxy(struct _wxycoord pwxyl, struct
_wxycoord pwxy2, char _far *imagen);
long_far ~magesize_w(double wxl, doublewyl, doublewx2,
doublewy2);
long_far ~magesize_wxy(struct _wxycoord pwxyl, struct
_wxycoord pwxy2);
void~far _putimage_w(double wxl, doublewyl, doublewx2,
doublewy2, char _far *imagen, short accion);
El siguiente ejemplo representa un conjunto de valores sobre un siste-
ma de coordenadas cartesianas. La representaci6n sehace 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 semuestra en
la figura siguiente:
I .,~.~n
1~l)lor ;:
Color J
Color" :
Coler 5 I '
Coler G !L
r.nl.l' 7 i,-
CIJ lul' 8
l~ill.Rr~
Color 10 i
Coler 11
Coler lZ 'L. -
Co l e r 13
r.nl.l' 14
Cu l . r 15
I~olo! ;:
Color J
Color"
r.nloT!i
Cul.r ,
r.nl.r 7
CIJ lul' 8
Color !I
Color 10
Color 11
r.nl.r 12
Cu l . r 13
r.nl.r 15
J . ll ,ur
L:Dlor ~
Color 3
[;0111I' !I
G o I . . 1i
Col.' Ii
r.nl. i
Culur B
r.nlnr ~
Color 10
[;01.' 11
G o l . . l Z
CDI 13
r.nl.14
Cui I!I
.I
-=
__ 1. ...
,"","'.'
~ __ '~,L
.. . IT""
~-r-
-I
I
( I
I
\l \
I
I
,I
,
I I
Microsoft C dispone deunas pocas funciones que permiten presentar gni-
ficamente un conjunto dedatos. Una presentaci6n gnifica sepuede hacer
utilizando 10ssiguientes tipos dediagramas: diagrama debarras horizon-
tales, diagrama debarras verticales, diagrama desectores, diagrama deli-
neas y diagrama de puntos.
ESTRUCTURA DE UN PROGRAMA PARA PRESENTACIONES
GRAFICAS
Para escribir un programa C queutilice funciones para presentaciones gra-
ficas, seguir 10ssiguientes pasos:
1. Incluir 10sficheros GRAPH.H y PGCHART.H, asi como cual-
quier otro fichero .h que necesite el programa.
2. Activar lamodalidad devideo para grcificos(----.Setvideomode())
e inicializar el sistema de presentaciones grcificas,
-pg_initchart( ).
3. Almacenar enuna estructura detipo chartenv,losparametros que
definen la presentacion grafica sobre la pantalla. La funcion
-pg_dejaultchart( ) los asigna por defecto.
La definicion deesta estructura, as! como delas estructuras
quelacomponen, seencuentran declaradas enel ficheropgchart.h.
Todas estas estructuras pueden visualizarse facilmente atraves del
menu de ayuda del PWB de Microsoft C; sera necesario acceder
asusmiembros cuando deseemos modificar losvalores 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 arepresentar enarrays, yaquelas funciones
parapresentaciones graficas losreferencianmediante punteros. Los
datos pueden provenir de diferentes medios: de ficheros, de cal-
culos 0directamente del teclado.
Estas funciones devuelven un 0 si seejecutan satisfactoriamente y un
valor distinto de 0, en caso contrario.
Inicializa el color y estilos delinea, paletas, modos depantalla 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 esun valor 102. Cada uno delos cinco tipos degnificos, 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 secorresponden 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 serepresen-
tan los valores.
explotar array denvalores 0 6 1.Un 1indica separar (explotar) esesec-
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 I ementos,va I 0res,nserieS,n,coI umnas,
eti_series)
Genera un gnifico para multiples seriesdedatos. El diagrama puede ser
debarras, delineas, 0depuntos, 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 quecontienelos elementospara loscualesserepresen-
tan los valores.
eti_series array deetiquetas correspondientes alosvaloresqueseindi-
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 ri eS,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 deetiquetas correspondientes acada una delasseriesre-
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 0Roman, 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 10sdistintos tipos de letras seencuen-
tran en 10sficheros 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 10sfonts 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( ).
Leelainformaci6n decabecera delos ficheros .FON especificados ycons-
truyeuna listacuya finalidad esdar informaci6n delos ficheros .FON dis-
ponibles.
Esta funci6n devuelvecomo resultado el numero defonts registrados
en la lista en memoria 0 un valor negativo si ocurre un error.
Busca un tipo deletra (font) que coincida con el conjunto decaracteristi-
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 0roman.
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, seutiliza. Si esta opci6n no
seespecifica 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 deletras (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 secorresponde
con un pixel de la pantalla. La segunda tecnica genera los tipos Modern,
Script y Roman a traves de un mapa de vectores, representando cada 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 queserequierepara escribir conlafuncion _outgtext( )
el texto en e1tipo de letra actual.
Esta funcion devuelve e1ancho en pixels del texto a visualizar, 0un
-1 si e1font no existe.
Esta funcion devuelveenuna estructura detipo -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 sobrelapantalla enel tipo deletra actual y enlaposicion
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 seentra enel entorno deprogramaci6n, mediante laorden PWB,
10primero que aparece es el menu principal y la ventana de edici6n. Las
partes que componen la pantalla del PWB se detallan a continuaci6n.
I. :
He l p: f se e k
" De scr i pt i o n. " ExaMpl ~ - 4l J ~<o nt e nt s. " I nde ~ " Back.
Synt ax: i nt f se e k( F I LE wst r e aM, l o ng o f f se t , i nt o r i gi n ) .
o r i gi n: SEEK_CUR. SEEK_END. SEEK_SET
II S~I~'S"'II;J qa;HI~'
f se e k( pf , de sp, SEEK_SET) ;
f r e ad( ar e g, byt e sr e g, 1. pf ) ;
pr i nt f ( " No Mbr e : %s' n" , r e g. no Mbr e ) ;
pr i nt f ( " No t a: %d' n' n" , r e g. no t a) ;
}
/ w Si se ha pu l sado u na t e cl a no val i da w/
i f ( ! c) f f l u sh( st di n) ;
Es laprimera linea; en ella sevisualizan los nombres delos 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 cada 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 0minusculas). 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 sepresentara 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, sepuede proceder de cualquiera de las dos
formas siguientes:
Pulsar la tecla correspondiente a la letra que sepresenta 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 unas flechas que indican el desplazamiento imaginario de
la pantalla sobre el texto cuando se efectua la accion de scroll y un cursor
que sedesplaza a10largo 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 alaizquierda
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, secoloca el cursor del ra-
t6n sobre la linea de scroll vertical, entre el cursor de scroll y la
flecha inferior 0entre el cursor de scroll y la flecha superior, y
sepulsa el bot6n izquierdo del rat6n. Laoperaci6n esanaloga 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 seapunta al cursor de scroll vertical de la ventana de texto y
setira, con el bot6n izquierdo del rat6n pulsado, hacia arriba 0
hacia abajo arrastrandolo sobre lalinea descroll, el texto sedes-
plaza hacia abajo 0hacia arriba. La acci6n sevecuando sedeja
depulsar el bot6n. Esta misma operaci6n sepuede realizar sobre
lalineadescroll horizontal para desplazar el textohacialaizquieraa
o hacia la derecha.
Es posible variar el tamafio deuna ventana. Para ello, seapunta ala
linea deseparaci6n entre ventanas y con el bot6n izquierdo del rat6n pul-
sado, setira enladirecci6n apropiada para agrandar 0 reducir laventana.
Para activar una ventana, apuntar acualquier lugar dentro delamis-
ma y pulsar el bot6n izquierdo del rat6n.
Para activar 0desactivar cualquier acci6n dentro de una ventana de
dialogo, apuntar al espacio entre corchetes 0entre angulos y pulsar el bo-
t6n izquierdo del rat6n.
Al ejecutar las 6rdenes seguidas por tres puntos (...) delos menus citados
anteriormente, sepresenta una ventana denominada ventana de dhilogo,
quepuede contener cuestiones para responder y opciones para elegir. Para
pasar deuna cuesti6n u opci6n a otra, pulsar latecla Tab. Una vez situa-
dos, podremos elegir 0 eliminar una opci6n con las teclas demovimiento
del cursor. Tambien, y mas faci!, pulsando latecla correspondiente alale-
tra en alto brillo decualquiera delas opciones, activamos 0desactivamos
dicha opci6n, 0 bien, nos situamos en la correspondiente cuesti6n a res-
ponder.
Desde estemenu, entre otras cosas, sepueden crear nuevos ficheros, car-
gar ficheros ya existentes, fusionar ficheros, salvar ficheros, visualizar el
siguiente fichero enlalista, renombrar un fichero, escribir todo 0 parte de
un fichero 0salir al DOS.
Search Make Run Options
,---------------,- c' ,Cf,m"S
~
Ope n .
Me r ge .
Ne xt
Save
Save As ...
Save Al l
Cl o se
Pr i nt . . .
DOS She l l Shi f t +F 9
{5 3. 1. 41. 8. 19. 5 . 13. 7. 10. 8. 20. o };
S]=
. . .G r e e i a . . . . . Tu ni e i a . . . . . Tu r qu l . a. . . . . Ot r o s . .};
] = {O. 1. O. O. O. 0 }; ~xi t I ' l l t +F 4
1 PROG 03. C Al l +l
2 PROG 02. C Al t +Z
3 <UNTI TLED> Al t +3
~ ~I
pe n a Ne w EMpt l j F i l e C N 00001. 001
/ M Mo do gr af i e o de MaS al t a r e so l u e i 6n M/
i f ( ! _se t vi de o Mo de ( _MAXRESMODE
e xi t ( 1 ) ; / M G r af i e o s no di spo ni bl e s M/
Crea un nuevo fichero denominado UNTITLED enmemoria. 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 sedesea cargar, sepuede proceder de
cualquiera de las formas siguientes:
Make Run Options- Brow
C,Cb00,SOURCE,PROG01.[
1. Escribir e1nombre del fichero acontinuacion deFile Name ypul-
sar Enter.
2. Pulsar latecla Tab para situar el cursor sobre lalista de ficheros
(File List) y elegir el deseado, bien con las teclas de movimiento
del cursor, 0 bien pulsando lainicial del nombre una 0 mas veces,
yaquepuedehaber inicialesrepetidas. 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 vccesconsecu-
tivas para cargarlo.
Para cambiar de directorio, seleccionar uno de los de la lista Drives
/ Dirs: (aparecen en letras mayusculas) y pulsar Enter; 0bien, escribir a
continuacion delapregunta 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 deotro fichero, encima delalinea sobre laque seen-
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 0ha sido modificado, se
nos preguntara si queremos salvarlo.
Permite escribir por la impresora, todo el contenido del fichero que seesta
editando, 0solamente el texto seleccionado.
Uinclude <conio.h>
u i ncl u de <st dl i b. h>
Ui ncl u de <gr aph. h>
u i ncl u de <st r i ng. h>
l I i ncl u de
u de f i ne
f l o at f
char _f a
{
sho r t _f
< OK ) <Cancel> < Help>
. ~
f1=Hfdp Enter E,;c=Canccl Tab=Next field C 0000l.mll
/ N Mo do gr af i co de MaS al t a r e so l u ci 6n N/
i f ( ! se t vi de o Mo de ( MAXRESMODE
e xi t ( 1 ) ; - / N G r af i co s no di spo ni bl e s 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 depro-
gramaci6n, son las siguientes:
Movimiento del cursor acualquier parte delapantalla, para modi-
ficar 0insertar texto.
Manipulaci6n debloques; esto es, mover, duplicar 0 borrar un blo-
que de texto.
Muchas de las 6rdenes que seexponen a continuaci6n requieren la selec-
ci6n previa de un conjunto de caracteres, palabras 0lineas. Para realizar
esta operaci6n dentro de la ventana activa, colocar el cursor al principio
del texto aseleccionar y manteniendo pulsada latecla Shift, marcar el tex-
to desplazando el cursor con las teclas de movimiento.
Tambien esposible 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 alapalabra y pulsar dos vecesconse-
cutivas el bot6n izquierdo del rat6n.
Apuntar alaprimera columna delalineay con
el bot6n izquierdopulsado tirar haciaabajo 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 seselecciona laopcion Edit del menu principal para suejecucion,
se presenta en pantalla un menu con las siguientes ordenes:
- Edit I.
I
Cut ~hI ftDP!
COPlj I :trl-I n',
P,,<:t P. <;h 1 ft I n',
Cl p.le fl,'1
Se t Ancho r
Se l e cl To Ancho r
. "
Unclo the 11::l co III ny r Or1r11rln IY.( lltin N UUUUl WJ l
Esta orden seejecuta despues deUndo. Cuando seejecuta Redo el fichero
vuelve a la forma que tenia antes de ejecutar la ultima orden Undo.
Esta orden borra el texto seleccionado y 10salvaenuna memoria interme-
dia (ver 6rdenes para seleccionar texto).
Copia enlaventana activa el texto almacenado enlamemoria intermedia.
Podemos realizar las dos operaciones siguientes:
Reemplazar texto: seleccionar el texto que deseamos reemplazar enla
ventana activa y ejecutar Paste.
Insertar texto: mover el cursor alaposici6n deseada dentro delaven-
tana activa y ejecutar Paste. El textose inserta a continuaci6n del cursor.
Esta orden borra el texto se1eccionado y no 10salva en la memoria in-
termedia.
Salvalaposicion actual del cursor como una marca (similar alaorden AKB
deWordStar). Mas tarde, estamarca 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.
Permitealternar entrelosmodos: caja, lineayflujo. En modo caja, sepuede
seleccionar una region rectangular detexto. En modo linea, sepueden se-
leccionar solo lineas. En modo flujo, laseleccion sehacesiguiendo el texto
ASCII.
Coloca el editor en modo delectura solamente con 10que el texto queda
pro~egido contra posibles cambios.
Permite definir el nombre para una macro y las teclas para referirseaella.
Para asignar lasteclas, mover el cursor a[ J ypulsar latecla0 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 lamacro, ejecutando laor-
den Edit Macro.
Bloques detexto pueden ser movidos 0 copiados conlas6rdenes Cut, Copy
y Paste. Para ello seguir los pasos que seindican a continuaci6n:
2. Pararealizar laoperaci6n demover, ejecutar laordenCut del menu
Edit.
Para realizar la operacion de copiar, ejecutar la orden Copy del
menu Edit.
En ambos casos, el texto seleccionado essalvado enuna memoria
intermedia.
3. Mover el cursor allugar donde sequiere insertar el texto. Estelu-
gar puede estar en:
otro fichero. Utilizar la orden Open para cargar el fichero en
memoria 0 seleccionarlo de la historia.
Cuando seeligela opcion View del menu principal sepresenta 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 sehace por
la posicion del cursor. EI minimo tamafio es de 5 lineas.
Dividelaventana activa dearriba aabajo. Ladivision sehace por laposi-
cion del cursor. EI minima tamafio es de 10 columnas.
Permite agrandar 0acortar laventana activa. Para utilizar estaorden debe
haber al menos dos ventanas abiertas.
Cierra laventana activa. Para utilizar esta orden debe haber al menos dos
ventanas abiertas.
Abre y cierra laventana de errores durante lacompilacion. Para localizar
la linea del programa donde seha producido un error:
2. pulsar Shift +F3 para moverseal siguiente error 0 Shift +F4para
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 I e ct e d Te xt f 3
Re pe at Last f l nd f 3
Change .
f o r F I l e .
Ne xt Er r o r Shl f t f 3
Pr e vI o u s Er r o r Shl f t +F 4
Se t Er r o r
G o To Mar k .
De f I ne Mar k .
Se t Mar k f I l e .
.1
f Incl c;lCllly UI' l'p~J uld(~ P"PIf'~.~iun f'<".f-'udo N nnnOl.nnl
Permite buscar un canicter, una palabra 0un 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 0escribiendolo) 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 Buscadesdeel cursor hasta el final del fichero y desdeel 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 separa en la primera ocurrencia. (Find All).
Cuando se requiere utilizar expresiones regulares, pueden utilizarse
como comodines 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 delos corchetes pueden utilizarse los caracteres espe-
ciales:
para indicar los limites del conjunto decaracteres que sedesea
especificar.
\ cualquier caracter delos indicados precedido por estecaracter, pier-
desusignificado especial y esconsiderado como un caracter normal.
Esta orden permite buscar un texto seleccionado previamente. Los pasos
a seguir son los siguientes:
1. Seleccionar el texto que sequiere buscar (ver 6rdenes del editor
para seleccionar). El texto seleccionado debe de estar sobre una
unica linea.
Tanto utilizando Find como Selected Text sepuede repetir la ultima
busqueda, pulsando F30ejecutando laorden Repeat Last Find deestemis-
mo menu.
Esta orden permite repetir la ultima busqueda realizada por Find 0por
Selected Text. La busqueda puede realizarse hacia adelante (F3) 0hacia
atnis (F4).
1. Introducir (selecciomindolo 0escribiendolo) el texto a buscar a
continuaci6n de la pregunta Find What:.
2. Introducir el texto quevaasustituir al anterior acontinuaci6n 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
l I i ncl ude ,
l I i ncl ude ' g
l I i ncl ude ,
l I i ncl ude <
l I def i ne PAl
f l oat f ar
char f ar
-{"It
shor t _ f ar expl ot ar [ PAI SESl
~M Modo gr af i co de Mas al t a r esol uci bn M~
i f (! set vi deoMode( MAXRESMODE
exi t( 1 ) ; - ~M Gr af i cos nO di sponi bl es 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 0a 10largo delaestructura en arbol delos di-
rectorios y partiendo de uno determinado.
Mueveel cursor alalinea fuente que contiene el siguiente error que seha
producido en la compilaci6n.
Mueveel cursor alalinea 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 lasventanas fuente yladeerrores, para que lalinea fuente que
contiene el error aparezca en la ventana activa. La orden Set Error esta
disponible si el cursor estaenlaventana deerrores decompilaci6n. En otro
caso, no tiene efecto.
Mueveel cursor auna marcadefinida conlaordenDefine Mark. Una marca
es un nombre asociado con una posici6n (fila y columna) en un fichero.
Hay dos formas para afiadir el contenido total 0parcial deotros ficheros
al fichero actual que se esta editando.
Para copiar un fichero entero dentro del texto actual, seutiliza la orden
Merge del menu File. El nuevo texto seinserta encima delalinea sobre la
que esta el cursor.
Para copiar parte deun fichero dentro del texto actual, realizar los siguien-
tes pasos:
1. Ejecutar laorden Open ... del menu File para cargar el fichero que
contiene el texto que queremos copiar, 0recuperar el fichero de
la historia.
3. Ejecutar una delas 6rdenes Cut 0Copy del menu Edit, para sal-
var el texto seleccionado en la memoria intermedia.
4. Ejecutar laorden Open ... del menu File para cargar el fichero en
el cual queremos copiar el texto seleccionado, 0recuperar el fi-
chero de la historia y situar el cursor en el lugar adecuado.
5. Ejecutar laorden Paste del menu Edit. El texto seinserta aconti-
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 0mas ficheros fuente conocidos como
modulos.
Los programas deun 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, sepuede
crear una Iista con los nombres delos mismos, mediante laorden Set Pro-
gram List ... del menu Make, yacontinuaci6n seprocede alacompilacion
deestalista. Esta orden seestudia acontinuaci6n. Esta lista sera utilizada
par Microsoft C para reconstruir el programa, cuando cualquiera desus
m6dulos haya sido modificado. Otra posible soluci6n, aunque no tan co-
moda, seria construir una libreria, cuesti6n que fuevistaenel capitulo co-
rrespondiente a "Librerias y utilidades del compilador".
Cuando sedige laopci6n Make del menu principal, sepresenta un menu
con las siguientes opciones:
uinclude <conio.h>
Ui ncl ude <st dl i b. h>
Ui ncl ude <gr aph. h>
Ui ncl ude <st r i ng. h>
Ui ncl ude <pgchar t . h>
Set Pr ogr aM LI st . . .
Edi t Pr ogr aM Ll st . . . PROG
Cl ear Pr ogr aM Ll~t
Udef i ne PAl SES &
f l oat _ f ar val or [ PAI SESl ={53. 1. 41. 8. 19. 5. 13. 7. 1a. 8. za. &};
char f ar Hel eMent os[ PAI SESl =
- {. .I t al i a . . . . . Espana . . . . . Gr eci a . . . . . Tuni ci a . . . . . l ' ur qui a. . . . Ot r os" };
shor t _ f ar expi ot ar [ PAI SESJ = {a. 1. a. a. a. a };
/ H Modo gr af i co de Mas al t a r esol uci 6n H/
i f (! set vi deoMode( MAXRESMODE
eXi t ( 1 ) ; - / H Gr af i cos no di sponi bl es */
~ ~
COMpI l e cur r ent sour ce f I l e C 00003. 059
Esta orden crea un fichero objeto (.obj) del m6dulo actual, pero no efec-
tua e1enlace (link).
Esta orden compila yenlaza todos losm6dulos pertenecientes al programa
actual que hayan sido modificados desde laultima vez que secompilaron
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 yenlaza todos los modulos pertenecientes al programa
actual (program list), creando un unico fichero ejecutable (.exe).
Esta orden creauna nueva listademodulos para un programa 0carga una
lista ya existente.
Antes deproceder como seindica acontinuacion, elegir el tipo defi-
chero que sedesea construir. Para ello ejecutar laorden 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 cadauno deelloslospuntos queacontinuacion seindican:
a) Ejecutar laorden Newdel menu presentado par Filepara crear
un nuevo fichero fuente, 0ejecutar laorden Open deestemis-
mo menu para editar un fichero que ya existe.
2. Ejecutar laorden Set Program List del menu presentado por File,
para crear la lista del programa 0para cargar una ya existente.
3. Ejecutar laorden Edit Program List del mismo menu, para afia-
dir y/o eliminar modulos delalista(Add/Delete) y salvar lamis-
ma (SaveList). Esposible tambien, afiadir ficheros objeto ylibre-
rias .LIB a la lista.
5. Finalmente, compilar, ejecutar ydepurar el programa formado por
esa lista de modulos.
Cuando secompila un programa farmado por multiples modulos, Mi-
crosoft C verifica lalista del programa para ver si algun modulo ha cam-
biado desdelaultima vezqueel programa fuecompilado. Si esas!, Micro-
soft C vuelveacompilar losmodulos quehan cambiado antes dereconstruir
el programa.
Lalista del programa esguardada enun fichero enel disco, quetiene
el mismo nombre del programa yextension .mak. Fuera del entorno deMi-
crosoft C (PWB), el fichero .mak seralaentrada para lautili dad NMAKE.
Esta arden permite mantener la lista de modulos que componen un
programa.
lista de 10s ficheros fuente existentes en e1directorio
especificado.
esta opdon afiade e1fichero se1eccionado a1alista del
programa 010elimina de 1alista. Si e1fichero no esta
en 1alista 10afiade y si esta 10e1imina.
salva todos 10scambios efectuados en 1alista de mo-
du10s que componen e1programa en un fichero con
extension .mak.
Cuando seelige la opci6n Run del menu principal, sepresenta un menu
con las siguientes opciones:
Run DOS C... J !'l'uf,d ...
[U=.tC'11=E r-"lenu..
. .
[ , ecul e t he cur r ent pr 0gr " ~ p~~I J ~- N OOOQl . nQl
Permite introducir argumentos para ser interpretados por el programa cuan-
do seejecute laorden Ejecute 0laorden Debug. Esta orden esequivalente
a introducir los argumentos en la linea de 6rdenes de DOS.
Permite ejecutar cualquier orden DOS sinsalir 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 desdedentro dePWB sintener queespecificar el nom-
bre del programa y sus argumentos cada vez.
La orden Customize Menu presenta un menu con las siguientes op-
dones:
Opt i ons
Iiiiiiii
t -
Rul l el Opt i on, ; . . .
Hr/)w~.e Optiuns ...
C LUMpl l cr Opt l un~ . . .
LINK Opt I ons .
NMAKE Opt I onS .
CodeUl ew Opt I onS . . .
. ~
~et envI r onMent opt I ons pseuel o N 00001. 001
Define los directorios donde buscar los ficheros decabecera (.h), las libre-
rias (.lib) y los ficheros deayuda (.hlp) deMicrosoft C. Estos caminos de
busqueda normalmentte son definidos utilizando variables deentorno del
DOS.
Permite asignar teclas que ejecutan 6rdenes, macros y funciones. Esta in-
formaci6n esalmacenada enun pseudofichero (area dememoria que nun-
ca es escrita sobre el disco).
Permite realizar cambios enel pseudofichero que contiene el conjunto ac-
tual deasignaciones hechas para el editor. Ejecutar laorden Save, para sal-
var los cambios efectuados en el fichero TOOLS.IN!.
Utilizar esta orden para: indicar si el programa vaaser depurado (Debug)
o no (Release); seleccionar el directorio destino delos ficheros resultantes
de la construccion del programa; seleccionar el lenguaje fuente; seleccio-
nar opciones predefinidas para laconstruccion 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 listademodulos utilizando laorden SetProgram List
del menu Make. Estos son los ficheros que formaran la base de
datos.
2. Ejecutar laorden BrowseOptions del menu Browsey seleccionar
la opcion Generate Browse Information. El resto de los campos
son opcionales.
3. Construir el programa utilizando lasordenes 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 10tanto, 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
esel nivel, mayor eslainformaci6n quenos dael 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 deinstrucciones 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 secompila para
depurarlo.
Show Debug Options...
Mostrar las opciones utilizadas por defecto, cuando
un programa secompila para depurarlo.
Set Release Options...
Afiadir opciones cuando unprograma secompila nor-
malmente.
Show Release Options...
Mostrar las opciones utilizadas por defecto, cuando
un programa se compila normalmente.
Las 6rdenes deestemenu permiten analizar un programa, atraves desus
declaraciones y funciones.
ni ncl ude <coni o. h>
ni ncl ude <st dl i b. h>
ni ncl ude <gr aph. h>
ni ncl ude <st r i ng. h>
ni ncl ude <pgchar t . h>
. . I, .
l~olo h' ef er ence.
Uip.IJ h'el::"\ti()n~-:hip.
Li"t. Hpfprpncl-'~;.
[ al l Tree.
,
Out l i ne . .
Next [ t r l Nl l Mo
Pr evi ou: : : : ; Cll~l'Nun
.[""",
S~n~;il iVp
Spl it. WiIldow~.;
ndef i ne PAI SES D
f l oat f ar val or [ PAI SES] ={53. 1. 41. 8. 19
char f ar Mel eMBnt os[ PAI SES] =
- {. .I t al i a . . . . . Espai l a. . . . . Gr eci a . . . . . Tuni ci
shor t _ f ar expl ot ar [ PAI SESJ = {B. 1. B,
+-- ~ tl
'nd deflnll.l0n(sl of SLJ Mbol [ 00004.0/4
~M Modo gr af i co de MdS al t a r esol uci 6n M/
i f (!set vi deoMode( MAXHESMODE
exi t ( 1 ) ; - / M Gr af i cos no di sponi bl es M/
Visualiza una lista de ficheros y numeros de linea donde seha echo refe-
rencia a los simbolos utilizados en el programa. Con esta orden podemos
encontrar la referencia para cualquier funci6n, variable, tipo 0macro.
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 enarbol, lacual permitever que funciones llaman
otras funciones.
Visualiza un fichero queno esteenlinea. Si introducimos un *.*, entonces
se visualizan todos los ficheros.
Cuando seseleccionaestaopcion, aparecen dos ventanas: unapara el Brow-
ser y otra para la ventana activa.
EI menu deayuda aparece cuando sepulsan lasteclas AU + H. En el apare-
cen las clases de ayuda, que a continuacion describimos.
/H Modo 9r af i co de MaS al t a r esol uci 6n H/
i f (!set vi deOMode( MAXRESMODE
exi t( 1 ) ; - /H Gr i l f i cos
~
r . ont eot s Shlft"FI
TopI c' ch~r t env FI
Hel p on Hel p
Nex t Chll rlenv
- P9_ def aul t char t (ent . _ PG_ BARCHART. _ PG_ PLAI NBARS) ;
st r cpy(ent . Mai nt i t l e. t i t l e. " Pr oducci 6n de acei t e de ol i va" ) ;
- P9_ char t (ent . el eMent os. val or . PAI SES) ;
get ch( ) ;
_ cl ear scr een(_ GCLEARSCREEN) ;
- P9_ def aul t char t (ent . _ PG_ COLUMNCHART. PG PLAI NBARS) ;
st r cpy(ent . Mai nt i t l e. t i t l e. " Pr oducci 6n de acei t e de ol i va" ) ;
- P9_ char t (ent . el eMent os. val or . PAI SES) ;
~ ~
l)lew lnriex of hplp tOPICS r. 00017.0or,
Para obtener informacion acercadeuna orden deun menu cualquie-
ra, utilizar las teclas del cursor para elegirla y pulsar Fl 0apuntarla con
el raton y pulsar el boton derecho.
Para obtener informacion a cerca deuna palabra clave, un operador
o una funcion, poner el cursor sobre dicho elemento y pulsar Fl 0apun-
tarla con el raton y pulsar el boton derecho.
Indice alfabetico de todas las palabras reservadas utilizadas por el compi-
lador 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 0al fichero de ayuda de Microsoft C.
Para obtener esta ayuda:
Un ordenador IBM PC 0compatible conun sistemaoperativo DOS
version 3.00superior, 0OS/2 version 1.10superior.
Un procesador 8088minima 8Mhz. Serecomienda 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. E1contenido del fichero NEW-CONF.SYS debe ser
puesto en e1fichero CONFIG.SYS. Las 6rdenes contenidas en el
fichero NEW-VARS.BAT tienen que ejecutarse antes de arrancar
Microsoft C; si 10desea puede incluirlas en e1fichero AUTOE-
XEC.BAT. Renombrar TOOLS.PRE por TOOLS.INI.
Si durante el proceso deinstalaci6n seequivoca, interrumpa dicho pro-
ceso pulsando las teclas Ctrl +C y ejecute de nuevo el programa SE-
TUP.EXE. Estenunca borra ficheros delosdiscosdel paquete Microsoft C.
Para instalar Microsoft C inserte el disco SETUP en launidad A, cambie
a la unidad A (A: <Enter y emita la orden:
esta opci6n esuti! cuando una vez instalado Microsoft C
necesitamos afiadir mas librerias combinadas. Esto evita
el tener que realizar de nuevo la instalaci6n.
estaopci6n suprime lainformaci6n deayuda enviada ala
pantalla durante el proceso de instalaci6n.
Una vez ejecutada la orden SETUP, senos vapreguntando las cues-
tiones necesarias para realizar lainstalaci6n. Presentamos acontinuaci6n
un ejemplo de una posible instalaci6n.
IMPORTANTE: Si usted no haleido lainformaci6n 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 launidad dedisco desdelacual vaa 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 vaadesarrollar susprogramas. Sepueden 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 ybajo DOS. Elija el modo bajo
el cual vaa ejecutar sus programas. Sepueden especificar ambos modos.
i,Deseaconstruir librerias combinadas? Para ganar velocidad el compila-
dor C, para cadamodelo, junta laslibreriasindividuales enuna solalibreria.
Hay tres opciones para soportar las operaciones enpunta flotante. El emu-
lador utiliza el coprocesador matematico si esta presente; si no 10esta, 10
emula mediante software. Esta eslaopci6n par defecto. Lalibreria 80x87
utiliza el coprocesador matematico si esta presente; si no 10esta, 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 libreriasparaseismodelos diferentesdememoria. Usted
puede elegir cualquiera deellos0todos. Serecomienda seleccionar losmo-
delos que utilice mas a menudo.
Cuando SETUP finaliza laconstrucci6n delas librerias combinadas elegi-
das, las librerias fuente a partir de las cuales seha realizado la construc-
ci6n, residen aun enel disco. Elias pueden ser borradas al finalizar lains-
talaci6n para liberar espacio en el disco.
Include in combined libraries: GRAPHICS.LIB [N): Y
PGCHART.LIB[N): Y
l,Incluyeenlas librerias combinadas laslibrerias gnificas GRAPHICS.LIB
y PGCHART.LIB? La libreria GRAPHICS.LIB seutiliza para graficos
engeneral y lalibreria PGCHART.LIB seutiliza para presentaciones gia-
ficas (diagramas).
Si usted estaconforme conlasrespuestas pulse'N' para continuar. Si quiere
modificar alguna deIasrespuestas pulse 'Y'. Si usted quieresalir pulse 'Q'.
Si sevaautilizar un rat6n (Microsoft Mouse) responder 'Y' para instalar
el fichero MOUSE.COM.
Hay varios ficheros condocumentaci6n acercadeestaversi6ncomo READ-
ME.DOC. Nosotros Ieaconsejamos que Ios copie.
Si usted tiene el sistema operativo de IBM(R) PC-DOS 3.20, puede que
necesiteparchear el sistema operativo utilizando estos ficheros, para mani-'
pular errores en coma flotante.
Si ustedestaconformeconlasrespuestas, pulse'N' paracontinuar. Si quiere
modificar alguna delasrespuestas 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 estaconforme conlasrespuestas, pulse'N' para continuar. Si quiere
modificar alguna delasrespuestas pulse 'Y'. Si usted quiere salir pulse 'Q'.
A continuaci6n senos van pidiendo los discos, para copiar los fiche-
ros que componen el paquete Microsoft C enlos directorios especificados.
PARTE
8
Apendices
Ficheros .h de C
C6digos de Caracteres (ASCII)
Indice Alfabetico
A continuacion sedetallan 10sficheros decabecera (.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
toascii
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
DefinicioncompatibledeMicrosoftdelosmodosdelecturayescriturapara
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
atan 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
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 Espaci o
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
i l .
165 A5 N
166 A6 a
167 A7 0
168 A8
l,
169 A9
.- -
170 AA - - - ,
171 AB
Yz
172 AC Y 4
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 'i F
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
r
252 FC n
253 FD 2
254 FE

255 FF ( es pac i oen
bl anc o ' 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
KeysFI-FIO (disabled assoftkeys)
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 O J 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 l A
+= OD
V
18
Backspace OE Enter l C
Ctrl l D
:\
2B
A I E Z 2C
S I F 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
#define91, 392
#error 400
#if 396
#ifdef e #ifndef 399
#include91, 395
#line399
#pragma 400
#undef 395
_arc 758
_asm 620
Definici6ndemacrosenlenguaje
ensamblador 629
Liamando a funcionesC 633
Manipulaci6ndeinterrupciones635
Reemplazar una funci6n C 634
Saito a una etiqueta640
Trabajando conestructuras637
Trabajando conpunteros635
Utilizando elementosdeC enun bloque
Utilizandoy salvandoregistros630
_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
_setvideomoderows743
_setvieworg 745
_setviewport 747
-setvisualpage 744
_setwindow 775
_unregisterfonts800
_wrapon 762
abort 701
Abrir un fichero317,319,356, 357
abs296
Accesibilidaddevariables. Ambito 101
Accesoaleatorio 319,348, 366
Accesosecuencial 319
access679
acos293
Algoritmoshash 497
Animaci6n764
Animaci6ndeun objeto 766
Arboles449
Arbolesbinarios450
Borrado en arboles458
de busqueda 453
perfectamente equilibrados460
Recorrido de arbolesbinarios451
Argumentosen la linea de 6rdenes274
Arrays163
de cadenasde caracteres182
de estructuras200
de punteros233
Declaraci6n ge un array 164
dimimicos242
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
bdos653
BIND 617
bios.h 857
break 138
bsearch 305
Buffer asociado con stdin 176
Busqueda binaria 486
Busqueda de datos485
Busqueda secuencial 485
Cadenasde caracteres173
calloc 243
Camino 669
Camposde bits207
Caracter fin delinea ycaracter fin defichero 123
Caracteresde C 52
Caracteresespecialesy signosde puntuaci6n 53
cast 85
ceil 296
Cerrar un fichero 318, 323, 356, 363
cgets376
char 54
chdir 674
chmod 680
chsize 682
CL 550
Clasesde almacenamiento 103
Clasificaci6n de datos473
clearerr 124, 324
clock 300
close 363
Code View 596
Calls616
Compilar y enlazar un programa C para
depurar 597
con rat6n (n:lOuse)603
Edit 605
File 604
Invocando a Code View 597
Menusde Code View601
Opcionesde Code View 598
Options614
Run 611
Search 609
Seleccionando texto 604
View606
Watch 612
c6digosde salida 686
Colas432
Coloresen modo grafico utilizando CGA 736
Coloresenmodo grafico utilizando VGA, MCGA
y EGA 739
Coloresen 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
Constantes64
Constantesde caracteres67
Constantesenteras65
Constantesreales66
continue 149
Control del cursor 378
Conversi6n de tipos82
Conversi6n explicita del tipo deuna expresi6n 85
Convertir coordenadas fisicasa 16gicasy
viceversa 741
Coordenadas
fisicas740
16gicas740
realesen una ventana 775
cos294
cosh 295
cprintf 377
cputs377
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 constantes72
Declaraci6n defuncionesanivel interno yanivel
externo 108
Declaracion de funcionesfar 0near 530
Declaracion de una funci6n 95, 257
Declaraci6ndevariablesnear, far, huge0based529
Declaracionescomplejas248
Declaracionesy definiciones92
defined 397
Definici6n de una funci6n 96, 252
Depuracion 47; 596
Depurar un programa 45
Detecci6n de errores318, 324
direct.h 858
Directoriosy carninos669
div 302
do 143
dos.h 859
dosexterr 691
double 60
dup 369
dup2369
Edici6n 45
Edici6n de un programa 42
Ejecuci6n de procesos702
Ejecuci6n de un proceso 699, 702
ensamblador 619, 641
Entrada y salida estandar 110
Entrada/salida
caracter a caracter 326
con formato 336
de cadenasde caracteres332
palabra a palabra 330
utilizando registros0bloques338
enum 57
eof 361
errno.h 860
Espaciosen 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
graficas785
Estructura para almacenar la configuracion de
video 731
Estructuras 197, 413
Creaci6n de una estructura 197
Operacionescon estructuras200
execxxx712
EXEHDR 617
exit 702
exp 295
EXP 618
Expresiones94
condicionales79
de Boole 76
numericas73
de ficheros553
Extensiones
fabs2%
fclose 323
fcloseal1 323
fcntl.h 860
fcvt 191
fdopen 321
feof 325
ferror 324
fflush 124, 344
fgetc 328
fgetpos352
fgets333
ficherosde cabecera (.h) 91, 403
Ficherostemporales345
fileno 362
float 59
float.h 860
floor 2%
fopen 319
for 145
for buclesanidados 146
FP_OFF 653
FP-1)EG 654
fprintf 336
fputc 326
fputs332
fread 338
free 245
freopen 322
fscanf 336
fseek 348
fsetpos352
ftell 349
Funciones95
con un numero de argumentosvariable 276
gnificas742
intrinsecas401, 407
matemliticas292
para asignacion dimimica de memoria 240
para clasificacion yconversiondecaracteres193
para control de directorios674
para control de procesos701
para conversion de datos190
para entrada/salida 363
para la consola 373
para llamar al DOS 650
para lospuertosde E/S 384
para manipulacion de ficheros679
para manipular cadenasde caracteres183
para obtener 0poner atributos749
para presentacionesgnificas787
para representar distintostiposde letras797
para un sistemadecoordenadascartesianas779
predefinidasen C 292
prototipo 257
recursivas279
referentesal uso de pal etas748
re1ativasa configuracion 742
relativasa coordenadas745
fwrite 338
getch 125, 373
getchar 122
getche 125, 374
getcwd 676
getenv 720
gets175
getw 330
goto y etiquetas150
graph.h 860
halloc 246
hash abierto 499, 503
hash con overflow 501
hash. Eliminacion de elementos502
HELP MAKE 617
hfree 246
Historia del lenguaje C 39
huge 523
Identificadores 68
if 129
if anidamiento de sentencias131
if estructura 134
IUNK 568
Iniciacion de un proceso 698
Inicializacion de cadenas232
inp 385
inpw 386
Instalacion 848
int 56
int86650
int86x 651
intdos651
intdosx 652
Interconexion deentradasy salidasestandar 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
labs296
large 520
Letras, digitosy caracter de subrayado 52
Leyendo y escribiendo datos318
lfind 306
LIB 572
LIB con respuestasautomaticas 576
LIB en modo pregunta/respuesta 575
limits.h 861
LINK 560
LINK con respuestasautomaticas 566
LINK en modo pregunta/respuesta 565
Listascirculares436
Listasdoblemente enlazadas444
Listaslineales415
Operacionesbasicas418
Llamada a una funcion 97, 256
Llamando a un pr0cedimiento en ensamblador
desde C 644
local time 301
log 295
log10296
long 56
long double 61
longjmp 704
lsearch 306
lseek 366
ltoa 192
macros392
macros0 funciones408
malloc 240
malloc.h 861
manipulacion de bloquesde memoria 542
Manipulacion de ficheros316, 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
Menusde PWB 806
Metodo de insercion 477
Metodo de la burbuja 473
Metodo Quicksort 480
mkdir 675
Modalidades de video disponibles728
Modelosde meploria estandar 515
Modelosde memoria mixtos526
NMAKE 579
Caracteresque pueoen modificar macros585
Componentesdeuna descripcion defichero 591
con respuestasautomaticas592
Directrices588
Ficherosen linea 590
Inicializacion automatica. TOOLS.INI 593
Macros583
Macrosespeciales584
makefile 579
Opcionesde NMAKE 582
Prioridades 589
Pseudoobjetivos 592
Reglasde inferencia 586
Simbolosespeciales591
Sustitucionesen macros584
Nombresde ficherosy extensiones49
Nombresde tipos63
Numerospseudoaleatorios 158
Ca1culo de areasy volumenes161
onexit 703
Opcionesde CL 554
open 357
Operacionescon directorios673
Operador
#394
##394
coma 79
de direccion-de (&) 80
de indireccion (.) 80
sizeof (tamafto de) 80
Operadores73
aritmeticos74
de asignacion 77
de relacion 75
logicos74
logicospara manejo de bits76
unitarios76
Ordenacion de ficherosen disco 488
Acceso aleatorio 494
Acceso secuencial 488
outp 385
outpw 386
Overlays567
Palabrasclave69
Parametrosde inicializacion del puerto 664
Parametrospor valor 0 por referencia 267
Pasando argumentosa funciones98, 268
perror 326
Pilas427
pow 297
Preparando un programa simple 45
preprocesador 391, 405
Presentacion de la sintaxisde C 51
printf 110
Prioridad y orden de evaluacion 81
process.h 862
Programa C formado por multiplesficheros99
Programasresidentes692
Prompt 672
Punteros22, 219
a cadenasde caracteres227, 236
a estructuras247
a funciones288
a objetosde tipo no especificado (void) 225
a punteros233
basadosen su propio segmento 539
basadosen un segmento 514
basadosen un segmento constante 532
basadosen un segmento variable 533
basadosen void 538
basadossobre un puntero 536
Comparacion de punteros223
Creacion de punteros219
far 511
huge 514
near 511
nulos524
Operacion de asignacion 222
Operacionesaritmeticas222
Operacionescon punteros222
Operadores220
y arrays226
y segmentosde 64K 510
putch 374
putchar 122
putenv 720
puts175
putw 330
PWB 805
Browse 842
Caracteristicas del editor del PWB 814
con raton (mouse) 809
Copiar texto de otrosficheros829
Edit 818
File 811
menu principal 808
Help 844
Make 831
Moviendo y copiando texto 821
Operacionescon el editor 816
Options836
PWB
Run 835
Search 824
Seleccionando texto 815
Ventanasde 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
Rutinasen lenguaje ensamblador en linea con
sentenciasC 620
Salvar el programa 44
scanf 116
search.h 863
Secuenciasde escape 53
segread 653
Seleccionar la modalidad de video 729
Sentencia compuesta 0 bloque 94
Sentencia de asignacion 109
Sentencias94
Serviciosdel 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
Sintaxisde lassentenciasy funcionesde C 108
small 517
sopen 369
Soporte MS-DOS
para llamadasal sistema 687
para asignaci6n de memoria 540
para cadenasde caracteres542
para manipulaci6n debloquesdememoria 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 procesos700, 701
time 300
time.h 867
tiny 516
Tiposde datos53
Tiposde letras(fonts) 796
Tiposderivados61
Tiposestandar 86
Tiposfundamentales 54
tmpfile 345
tmpnam 346
toascii 195
tolower 195
toupper 196
typedef 63
umask 370
UNDEL 618
ungetch 375
Uniones201
unlink 682
Utilizaci6n de dispositivosestandar 334
Utilizaci6n de punterosbasadosen un
segmento 530
uti me 685
varargs.h 867
Variables70
dedaradas a nivel externo 104
dedaradas a nivel interne 106
globalesy locales101
Visualizar imagenes754
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
320pags.
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
540pags.
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 esuno de loslenguajes de programaci6n maspopulares en la
actualidad. permite realizar una programaci6n estructurada sin lfmite a la
creatividad del programador V ademasloscompiladoresC tienen la ventaja
de produci:r programas reducidos y muv rapidos en ejecuci6n. Por ello,
muchospaquetesde &oftwareestan escritosen C.
. Ellibro hasido escrito p~ns~ndo par una parte en laspersonasque no
tenie'ndo cono~imientosde programaci6n C, desean aprenderlos; y por otra,
para losprogramadorts expertos, loscualesencontraran una exposici6n
completa dellenguaje C con todaslascaracterfsticasque posee, V 10que con
el sepuede realizar. En resumen, incluve temasreferentesa:
Programaci6n C en base al disefio Top Down.
Sentencias! funcionesV punteros. :: .
Estructuras, uniones, enumeraciones, cadenasV arrays.
Ficheros. Acceso secuencial V aleatorio.
EI preprocesador.
Estrw:turasdinamicas.(listas V arboles).
Algoritmosrecursivos, de ordenaci6n V de busqueda.
Manejo de Iamemoria.
Compilador V eniazador ..
". Librerfasy utilidades.
Rutina~en Ienguaje ensamblador.
Serviciosdel DOS y del BIOS.
Graficos. .
Entorno integ,rado de desarrollo.
Todo esto y masse exp~mede forma clara y sencilla, con alrededor
de 175 PROBLEMAS RESUELTOS que Ieavudaran V serviran de base para
&USaplicaciones.
9 780201 625066
I SBN 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 F e 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 1050A, Venezuela

Vous aimerez peut-être aussi