You are on page 1of 153

AULA POLITCNICA / ETSETB

Marco A. Pea Basurto Jos M. Cela Espn

Introduccin a la programacin en C

EDICIONS UPC

Primera edicin: septiembre de 2000

Diseo de la cubierta: Manuel Andreu

Los autores, 2000 Edicions UPC, 2000 Edicions de la Universitat Politcnica de Catalunya, SL Jordi Girona Salgado 31, 08034 Barcelona Tel.: 934 016 883 Fax: 934 015 885 Edicions Virtuals: www.edicionsupc.es E-mail: edicions-upc@upc.es

Produccin:

CPET (Centre de Publicacions del Campus Nord) La Cup. Gran Capit s/n, 08034 Barcelona

Depsito legal: B-32.449-2000 ISBN: 84-8301-429-7


Quedan rigurosamente prohibidas, sin la autorizacin escrita de los titulares del copyright, bajo las sanciones establecidas en las leyes, la reproduccin total o parcial de esta obra por cualquier medio o procedimiento, comprendidos la reprografa y el tratamiento informtico, y la distribucin de ejemplares de ella mediante alquiler o prstamo pblicos.

Introducci n a la programaci n en C o o
Marco A. Pe a n Jos M. Cela e Departament dArquitectura de Computadors Universitat Polit` cnica de Catalunya e 08034 Barcelona, Espa a n marcoa@ac.upc.es cela@ac.upc.es 19 de junio de 2000

Indice General

Indice General
Indice de Figuras Indice de Tablas Prefacio 1 Conceptos b sicos de programaci n a o 1.1 Ordenador y perif ricos . . . . . e 1.2 Bits, bytes y palabras . . . . . . 1.3 Lenguajes de programaci n . . . o 1.3.1 Lenguajes de bajo nivel . 1.3.2 Lenguajes de alto nivel . 1.4 Elaboraci n de un programa . . o 1.5 Traductores . . . . . . . . . . . 1.5.1 Ensambladores . . . . . 1.5.2 Int rpretes . . . . . . . e 1.5.3 Compiladores . . . . . . 2 Primer contacto con C 2.1 Un poco de historia . . . . . . 2.2 Caractersticas del lenguaje . . 2.3 Creaci n de un programa . . . o 2.4 Primeros pasos con C . . . . . 2.5 El modelo de compilaci n de C o v vii ix 1 1 2 2 3 3 4 5 5 5 6 7 7 7 8 9 10 13 13 13 14 14 15 16 17 17 17

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

3 Empezando a programar 3.1 Identicadores . . . . . . . . . . . 3.2 Estructura de un programa . . . . 3.3 Variables y constantes . . . . . . . 3.3.1 Variables . . . . . . . . . 3.3.2 Constantes . . . . . . . . 3.3.3 Entrada y salida de valores 3.4 Expresiones . . . . . . . . . . . . 3.4.1 Operador de asignaci n . . o 3.4.2 Operadores aritm ticos . . e

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

Los autores, 2000; Edicions UPC, 2000.

Indice General

ii

3.5

3.4.3 Operadores relacionales 3.4.4 Operadores l gicos . . . o 3.4.5 Prioridad de operadores Ejercicios . . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

18 19 19 20 23 23 25 26 27 28 31 33 33 35 36 38 38 38 39 41 41 42 43 44 44 45 45 46 47 49 49 50 50 52 53 54 54 55 55 56 57

4 Construcciones condicionales 4.1 Construcci n if . . . . . . . . o 4.1.1 Variante if-else . . 4.1.2 Variante if-else-if 4.2 El operador condicional ? . . . 4.3 Construcci n switch . . . . . o 4.4 Ejercicios . . . . . . . . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

. . . . . .

5 Construcciones iterativas 5.1 Construcci n while . . . . . . . . . o 5.2 Construcci n do-while . . . . . . o 5.3 Construcci n for . . . . . . . . . . o 5.3.1 El operador coma (,) . . . . . 5.3.2 Equivalencia for-while . . 5.4 Las sentencias break y continue 5.5 Ejercicios . . . . . . . . . . . . . . . 6 Tipos de datos elementales 6.1 N meros enteros . . . . . . . u 6.1.1 Modicadores . . . . 6.1.2 Resumen . . . . . . . 6.2 Caracteres . . . . . . . . . . . 6.2.1 Caracteres especiales . 6.2.2 Enteros y el tipo char 6.2.3 Conversiones de tipos 6.3 N meros reales . . . . . . . . u 6.4 Ejercicios . . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

7 Tipos de datos estructurados: Tablas 7.1 Vectores . . . . . . . . . . . . . . 7.1.1 Consulta . . . . . . . . . 7.1.2 Asignaci n . . . . . . . . o 7.1.3 Ejemplos . . . . . . . . . 7.2 Matrices . . . . . . . . . . . . . . 7.2.1 Consulta . . . . . . . . . 7.2.2 Asignaci n . . . . . . . . o 7.2.3 Ejemplo . . . . . . . . . . 7.3 Tablas multidimensionales . . . . 7.3.1 Ejemplo . . . . . . . . . . 7.4 Cadenas de caracteres . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

Los autores, 2000; Edicions UPC, 2000.

iii

Indice General

7.5

7.4.1 Asignaci n . . . . . . . . . . . o 7.4.2 Manejo de cadenas de caracteres 7.4.3 Ejemplos . . . . . . . . . . . . Ejercicios . . . . . . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

57 59 60 61 63 63 64 65 65 66 66 67 68 69 70 70 71 71 72 75 75 76 76 77 78 79 83 84 87 87 89 89 90 91 91 91 91 92 93 94 94 95

8 Otros tipos de datos 8.1 Estructuras . . . . . . . . . . . . . . . . . . 8.1.1 Declaraci n de variables . . . . . . . o 8.1.2 Acceso a los campos . . . . . . . . . 8.1.3 Asignaci n . . . . . . . . . . . . . . o 8.1.4 Ejemplo . . . . . . . . . . . . . . . . 8.2 Uniones . . . . . . . . . . . . . . . . . . . . 8.2.1 Ejemplo . . . . . . . . . . . . . . . . 8.3 Tipos de datos enumerados . . . . . . . . . . 8.4 Denici n de nuevos tipos de datos . . . . . o 8.5 Tiras de bits . . . . . . . . . . . . . . . . . . 8.5.1 Operador de negaci n . . . . . . . . o 8.5.2 Operadores l gicos . . . . . . . . . . o 8.5.3 Operadores de desplazamiento de bits 8.6 Ejercicios . . . . . . . . . . . . . . . . . . . 9 Punteros 9.1 Declaraci n y asignaci n de direcciones o o 9.1.1 Declaraci n . . . . . . . . . . . o 9.1.2 Asignaci n de direcciones . . . o 9.2 Indirecci n . . . . . . . . . . . . . . . o 9.3 Operaciones con punteros . . . . . . . . 9.4 Punteros y tablas . . . . . . . . . . . . 9.5 Punteros y estructuras . . . . . . . . . . 9.6 Ejercicios . . . . . . . . . . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

10 Funciones 10.1 Generalidades . . . . . . . . . . . . . . . 10.2 Denici n y llamada . . . . . . . . . . . o 10.2.1 Denici n . . . . . . . . . . . . . o 10.2.2 Prototipos . . . . . . . . . . . . . 10.2.3 Llamada . . . . . . . . . . . . . . 10.3 Variables y par metros . . . . . . . . . . a 10.3.1 Variables locales . . . . . . . . . 10.3.2 Variables globales . . . . . . . . 10.3.3 Par metros formales . . . . . . . a 10.4 Devoluci n de resultados . . . . . . . . . o 10.5 Paso de par metros . . . . . . . . . . . . a 10.5.1 Paso de par metros por valor . . . a 10.5.2 Paso de par metros por referencia a

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Los autores, 2000; Edicions UPC, 2000.

Indice General

iv

10.5.3 Las tablas y las funciones . . . 10.5.4 Par metros en la funci n main a o 10.6 Recursividad . . . . . . . . . . . . . . 10.7 Ejercicios . . . . . . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. 96 . 99 . 100 . 101 105 107 109 111 111 113 113 113 116 119 119 119 120 122 123 123 124 124 124 126 127 128 129 129 129 135 135 135 136 138 141 143

11 Ficheros 11.1 Abrir y cerrar cheros . . . . . . . . . . . . . . . . 11.2 Leer y escribir en cheros . . . . . . . . . . . . . . 11.3 Otras funciones para el manejo de cheros . . . . . 11.3.1 feof . . . . . . . . . . . . . . . . . . . . 11.3.2 ferror . . . . . . . . . . . . . . . . . . 11.3.3 fflush . . . . . . . . . . . . . . . . . . 11.4 Ficheros est ndar: stdin, stdout, stderr a 11.5 Ejercicios . . . . . . . . . . . . . . . . . . . . . . A El preprocesador A.1 Directiva include . . . . . . A.2 Directivas define y undef . A.3 Directivas ifdef y ifndef . A.4 Macros . . . . . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

B La librera est ndar a B.1 Manipulaci n de cadenas de caracteres . . o B.2 Entrada y salida . . . . . . . . . . . . . . B.2.1 Entrada y salida b sica . . . . . . a B.2.2 Entrada y salida con formato . . . B.2.3 Ficheros . . . . . . . . . . . . . . B.3 Funciones matem ticas . . . . . . . . . . a B.4 Clasicaci n y manipulaci n de caracteres o o B.5 Conversi n de datos . . . . . . . . . . . . o B.6 Manipulaci n de directorios . . . . . . . o B.7 Memoria din mica . . . . . . . . . . . . a

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

C Sistemas de numeraci n o C.1 Naturales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.2 Enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.3 Reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.3.1 Problemas derivados de la representaci n en coma otante o D Tabla de caracteres ASCII E Bibliografa y recursos WEB

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

Los autores, 2000; Edicions UPC, 2000.

Indice de Figuras

Indice de Figuras
1.1 1.2 1.3 1.4 1.5 2.1 4.1 4.2 5.1 5.2 5.3 7.1 7.2 7.3 9.1 Niveles de abstracci n en los lenguajes de programaci n . . . . . o o Cronologa en el desarrollo de algunos lenguajes de programaci n o Ciclo de vida de un programa . . . . . . . . . . . . . . . . . . . . Fases en la interpretaci n de un programa . . . . . . . . . . . . . o Fases en la compilaci n de un programa . . . . . . . . . . . . . . o . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 3 4 5 6 11 24 29 34 35 36 50 54 56 82 99

Modelo de compilaci n de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . o Esquema de funcionamiento de if y de if-else . . . . . . . . . . . . . . . . . . . Esquema de funcionamiento de switch . . . . . . . . . . . . . . . . . . . . . . . . . Esquema de funcionamiento de while . . . . . . . . . . . . . . . . . . . . . . . . . Esquema de funcionamiento de do-while . . . . . . . . . . . . . . . . . . . . . . . Esquema de funcionamiento de for . . . . . . . . . . . . . . . . . . . . . . . . . . . Representaci n gr ca de un vector . . . . . . . . . . . . . . . . . . . . . . . . . . . o a Representaci n gr ca de una matriz . . . . . . . . . . . . . . . . . . . . . . . . . . . o a Representaci n gr ca de una tabla de tres dimensiones . . . . . . . . . . . . . . . . . o a Acceso a una matriz mediante un puntero . . . . . . . . . . . . . . . . . . . . . . . .

10.1 Acceso a una matriz mediante un puntero . . . . . . . . . . . . . . . . . . . . . . . .

11.1 Almacenamiento de un chero de texto . . . . . . . . . . . . . . . . . . . . . . . . . 106

Los autores, 2000; Edicions UPC, 2000.

vii

Indice de Tablas

Indice de Tablas
3.1 3.2 3.3 3.4 3.5 6.1 6.2 6.3 6.4 8.1 C.1 C.2 C.3 C.4 Palabras reservadas de C . . . . . . . . . . . . Operadores aritm ticos en C . . . . . . . . . . e Operadores relacionales y l gicos en C . . . . . o Tabla de verdad de los operadores l gicos en C o Prioridad y asociatividad de los operadores en C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 17 19 19 20 42 44 45 46 71 135 136 137 138

Representaci n de enteros en decimal, octal y hexadecimal o Resumen de tipos de datos enteros . . . . . . . . . . . . . Caracteres interpretados como enteros . . . . . . . . . . . Resumen de tipos de datos reales . . . . . . . . . . . . . .

Tabla de verdad de los operadores l gicos . . . . . . . . . . . . . . . . . . . . . . . . o Representaci n de n meros naturales en binario natural o u Representaci n de n meros enteros en complemento a 2 o u Representaci n de n meros enteros en exceso 2e;1 . . . o u Representaci n de n meros reales . . . . . . . . . . . . o u . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

D.1 Caracteres y sus c digos ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 o

Los autores, 2000; Edicions UPC, 2000.

ix

Pr logo o

Pr logo o
Este libro surge a partir de la experiencia docente de los autores en la asignatura Introducci n a los o ordenadores de la Escola T` cnica Superior dEnginyeria de Telecomunicaci de Barcelona, de la Unie o versitat Polit` cnica de Catalunya. Como su ttulo indica, se trata de un texto de introduci n a la proe o gramaci n en lenguaje C. El libro pretende ce irse a los aspectos fundamentales del est ndar ANSI C o n a actual. Aunque hoy en da existen otros lenguajes de programaci n muy populares como C++ o JAVA, o la comprensi n de estos lenguajes exige un s lido conocimiento de las bases de programaci n en C. o o o El texto est concebido como un curso completo y por lo tanto debe ser ledo de forma secuencial. a Al nal de cada captulo hay un conjunto de ejercicios propuestos. El lector debe tratar de resolver el mayor n mero posible de estos ejercicos. De igual forma es una buena pr ctica el programar los ejemu a plos resueltos en el texto. El lector debe recordar que la programaci n es una t cnica aplicada, igual que o e tocar un instrumento musical, y por lo tanto requiere muchas horas de ensayo para ser dominada. Los ejercicios propuestos son lo sucientemente simples como para no requerir conocimientos adicionales de otras materias (matem ticas, fsica, contabilidad, etc). a Uno de los puntos m s importantes para quien empieza a programar es adoptar desde el principio a un buen estilo de programaci n. Esto es, escribir las construcciones del lenguaje de una forma clara o y consistente. En este sentido, los ejemplos resueltos en el texto muestran un buen estilo b sico de a programaci n, por lo que se recomienda al lector imitar dicho estilo cuando realice sus programas. o Los ap ndices A y B son de lectura obligada antes de comenzar a desarrollar programas de complee jidad media. En dichos ap ndices el lector se familiarizar con el uso del preprocesador y de la librera e a est ndar. Ambas son herramientas fundamentales para desarrollar programas. a El ap ndice C incluye una descripci n de los formatos de datos en el computador. Este tema no e o es propiamente de programci n, pero la comprensi n de dichos formatos ayuda al programador a eno o tender mejor conceptos b sicos, como las operaciones de conversi n de tipos. El lector puede leer este a o ap ndice en cualquier momento, aunque recomendamos leerlo antes del captulo 6. e En las referencias bibliogr cas se indican algunas direcciones web donde el lector podr encontrar a a preguntas y respuestas comunes de quienes se inician en el lenguaje C. As mismo, el lector podr a encontrar materiales adicionales sobre programaci n, historia del lenguaje, etc. o

Los autores, 2000; Edicions UPC, 2000.

1. Conceptos b sicos de programaci n a o

Captulo 1

Conceptos b sicos de programaci n a o


1.1 Ordenador y perif ricos e
Un ordenador s lo es capaz de ejecutar ordenes y operaciones muy b sicas, tales como: o a Aritm tica entera: sumar, restar, multiplicar, etc. e Comparar valores num ricos o alfanum ricos e e Almacenar o recuperar informaci n o Con la combinaci n de estas operaciones b sicas, y gracias a su gran potencia de c lculo, el ordeo a a nador puede llevar a cabo procesos muy complejos. Sin embargo, en cualquier caso existe una estrecha dependencia del ordenador con el programador. Es el programador quien indica a la m quina c mo y a o qu debe hacer, mediante la l gica y el razonamiento previo, expresado en forma de un programa. e o En denitiva, el ordenador s lo es capaz de aceptar datos de entrada, procesarlos y facilitar otros o datos o resultados de salida. Los datos se introducen u obtienen del ordenador mediante los perif ricos e de entrada y salida. Estos son los encargados de facilitar la relaci n entre el coraz n del ordenador y el o o mundo exterior, y en particular los usuarios de ordenadores. Dependiendo de su funci n particular, los o perif ricos pueden clasicarse en: e Perif ricos de entrada: cuya funci n es facilitar la introducci n de datos y ordenes al ordenador: tee o o clado, rat n, l piz optico, lector de c digo de barras, esc ner, tableta digitalizadora, etc. o a o a Perif ricos de salida: cuya funci n es mostrar al exterior informaci n almacenada en memoria o los e o o resultados de las operaciones realizadas por el ordenador: pantalla, impresora, plotter, etc. Perif ricos de entrada y salida: capaces tanto de introducir como de extraer informaci n del ordenae o dor: discos y cintas magn ticos, discos opticos, etc. e Perif ricos de comunicaci n: encargados de establecer y facilitar el intercambio de informaci n entre e o o dos ordenadores: m dem, tarjetas de red (Ethernet, Token Ring, RDSI, . . . ), etc. o

Los autores, 2000; Edicions UPC, 2000.

1.2. Bits, bytes y palabras

Programador

Lenguaje natural

Lenguaje de programacin

Lenguaje mquina

Ordenador

Figura 1.1: Niveles de abstracci n en los lenguajes de programaci n o o

1.2 Bits, bytes y palabras


La unidad de memoria m s peque a en un ordenador se denomina bit (del ingl s binary digit). Puede a n e o tomar unicamente dos posibles valores: 0 o 1. En ocasiones, debido a la relaci n intrnseca con los valores en las se ales el ctricas en circuitos digitales, se dice que un bit est bajo o alto, o bien descon e a nectado o conectado. Como puede verse, no es posible almacenar mucha informaci n en un solo bit. o Sin embargo, un ordenador posee cantidades ingentes de ellos, por lo que podra decirse que los bits son los bloques b sicos con los que se construye la memoria del ordenador. a El byte, compuesto por ocho bits (algunos autores se reeren a esta unidad como octeto), es una unidad de memoria m s util. Puesto que cada bit puede tomar el valor 0 o 1, en un byte pueden represena o tarse hasta 28 = 256 combinaciones de ceros y unos (256 c digos binarios). Con estas combinaciones pueden representarse, por ejemplo, los enteros entre 0 y 255 (0 : : : 28 ; 1), un conjunto de caracteres, etc. La unidad natural de memoria para un ordenador es la palabra. Los ordenadores de sobremesa actuales, por ejemplo, suelen trabajar con palabras de 32 o 64 bits. En grandes ordenadores, el tama o n de la palabra puede ser mucho mayor, pero siempre formada por un n mero de bits, potencia de 2. En u cualquier caso, los ordenadores encadenan dos o m s palabras de memoria con el n de poder almacenar a datos complejos y, en general, de mayor tama o. n

1.3 Lenguajes de programaci n o


Un lenguaje de programaci n podra denirse como una notaci n o conjunto de smbolos y caracteres o o que se combinan entre s siguiendo las reglas de una sintaxis predenida, con el n de posibilitar la transmisi n de instrucciones a un ordenador. Dichos smbolos y caracteres son traducidos internamente o a un conjunto de se ales el ctricas representadas en sistema binario, es decir, s lo dos valores: 0 y 1. n e o Esta traducci n es necesaria porque el procesador s lo entiende ese lenguaje, al cual nos referiremos o o como lenguaje m quina. a

Los autores, 2000; Edicions UPC, 2000.

1. Conceptos b sicos de programaci n a o

COBOL FORTRAN LISP Ensamblador Algol BASIC PL/I Prolog SIMULA Logo

Forth C Modula-2 Smalltalk C++ Java Ada

Pascal

1950

1955

1960

1965

1970

1975

1980

1985

1990

1995

Figura 1.2: Cronologa en el desarrollo de algunos lenguajes de programaci n o

1.3.1 Lenguajes de bajo nivel


Se incluyen en esta categora aquellos lenguajes que por sus caractersticas se encuentran m s pr ximos a o a la arquitectura del ordenador, como el lenguaje m quina y el lenguaje ensamblador. a Lenguaje m quina a Cualquier problema que deseemos resolver se plantea en primer lugar en nuestro lenguaje natural. Sin embargo, para que la secuencia de pasos que resuelven el problema pueda ser entendida por un ordenador, debe traducirse a un lenguaje muy b sico denominado lenguaje m quina. a a El lenguaje m quina se caracteriza por ser el unico que es directamente inteligible por el ordenador, a puesto que se basa en la combinaci n de dos unicos smbolos (0 y 1) denominados bits. Adem s cada o a procesador posee su propio lenguaje m quina, por lo que un programa escrito en lenguaje m quina de a a un procesador X no podr , en principio, ejecutarse en un procesador Y. a Lenguaje ensamblador Constituye una evoluci n del lenguaje m quina. Se basa en la utilizaci n de mnemot cnicos, esto es, o a o e abreviaturas de palabras que indican nombres de instrucciones. Para programar en lenguaje ensamblador es necesario conocer en profundidad la estructura y funcionamiento interno del ordenador, as como dominar el uso de diferentes sistemas de numeraci n, como el binario, hexadecimal, octal, etc. o En general, los programas escritos en ensamblador requieren mucho menos espacio de memoria y se ejecutan m s r pidamente que si se hubiesen desarrollado en un lenguaje de alto nivel, puesto que a a est n optimizados para una arquitectura especca. Sin embargo, esto ultimo es un inconveniente, pues a causa que los programas no sean portables de un ordenador a otro con un procesador distinto.

1.3.2 Lenguajes de alto nivel


Se engloban aqu todos los lenguajes de programaci n que por sus caractersticas se asemejan m s al o a lenguaje natural del programador. Algunos de los m s conocidos son: FORTRAN, BASIC, Pascal, a Modula, C, Ada, Java, etc. (ver Fig. 1.2). La caracterstica m s importante de estos lenguajes es que son independientes de la arquitectura del a ordenador, por lo que un programa escrito en un lenguaje de alto nivel puede ejecutarse sin problemas

Los autores, 2000; Edicions UPC, 2000.

1.4. Elaboraci n de un programa o

Anlisis 11111111 00000000 11111111 00000000

Mantenimiento 11111111111111 00000000000000 11111111111111 00000000000000 Diseo Explotacin 1111111 0000000 00000000000 0000000 000000000000011111111111 1111111 111111111111111111111111 00000000000 Codificacin 1111111111111 0000000000000 1111111111111 0000000000000
Figura 1.3: Ciclo de vida de un programa

en otros ordenadores con procesadores distintos. Por ello, el programador no necesita conocer a fondo el funcionamiento del ordenador en el que programa, sino que el lenguaje le permite abstraerse de los detalles de bajo nivel. Esta abstracci n de la arquitectura de la m quina implica que todo programa o a escrito en un lenguaje de alto nivel deber traducirse a lenguaje m quina, de forma que pueda ser a a entendido y ejecutado por el ordenador. Para ello cada tipo de ordenador deber disponer de unos a programas especiales que realicen dicha traducci n (ver Sec. 1.5). o

1.4 Elaboraci n de un programa o


El desarrollo de un programa para solucionar un determinado problema inform ticamente puede resua mirse en el ya cl sico concepto de ciclo de vida. Este puede desglosarse en los siguientes pasos a seguir a secuencialmente: an lisis, dise o, codicaci n, explotaci n y mantenimiento (ver Fig. 1.3). a n o o

An lisis a
En la fase de an lisis se estudia cu l es el problema a resolver y se especican a muy alto nivel los a a procesos y estructuras de datos necesarios, de acuerdo con las necesidades del cliente. Para realizar un buen an lisis ser necesario interaccionar con el cliente y conocer a fondo sus necesidades. Antes a a de proceder al dise o es muy importante haber comprendido correctamente los requerimientos del n problema.

Diseno
Una vez bien denido el problema y las lneas generales para solucionarlo, se requiere una soluci n o adecuada a un conjunto de recursos determinado. Tanto fsicos: en qu ordenador va a funcionar la e aplicaci n, de qu tipo de perif ricos se dispone . . . , como l gicos: qu sistema operativo se usar , qu o e e o e a e herramientas de desarrollo, qu bases de datos . . . Finalmente se dise ar un conjunto de algoritmos e n a que resuelvan los distintos subproblemas en que se haya dividido el desarrollo.

Codicaci n o
Consiste en la traducci n de los algoritmos dise ados previamente, utilizando el lenguaje y entorno de o n desarrollo escogidos en la fase anterior. Ser necesario realizar pruebas que garanticen al m ximo la a a calidad de los programas desarrollados. Entre otras cosas, que est n libres de errores. e La documentaci n generada en esta fase junto con la de las fases anteriores ser muy util en el o a futuro para las eventuales actuaciones de mantenimiento.

Los autores, 2000; Edicions UPC, 2000.

1. Conceptos b sicos de programaci n a o

Instruccin 1 Instruccin 2 Instruccin 3 ...

Intrprete Intrprete Intrprete

Ejecucin 1 Ejecucin 2 Ejecucin 3

Figura 1.4: Fases en la interpretaci n de un programa o

Explotaci n o
Los diferentes programas desarrollados en la fase anterior se instalan en el entorno nal de trabajo. Si es necesario se instalar n tambi n otras herramientas de utilidad, necesarias para el correcto funcionaa e miento del sistema. Se debe proporcionar documentaci n, manuales de usuario, formaci n, etc. o o

Mantenimiento
En esta fase se realizar n correcciones al sistema desarrollado, bien para solventar errores no depuraa dos, bien para cambiar o a adir nuevas funcionalidades requeridas por el cliente. Dependiendo de la n importancia del caso, ser necesario retomar el ciclo de vida a nivel de codicaci n, dise o o incluso a o n an lisis (ver Fig. 1.3). a Cuanto mejor se haya documentado el desarrollo en las primeras fases del ciclo de vida, menor ser a el tiempo necesario para llevar a cabo los distintos tipos de mantenimiento.

1.5 Traductores
Como ya se ha comentado, el unico lenguaje directamente inteligible por el ordenador es el lenguaje m quina. Por ello, si se programa usando lenguajes de alto nivel ser necesario alg n programa traduca a u tor. Este, a su vez, ser el encargado de comprobar que los programas est n escritos correctamente, de a e acuerdo con la denici n del lenguaje de programaci n empleado. Pueden distinguirse varios tipos de o o traductores:

1.5.1 Ensambladores
Los programas ensambladores son los encargados de traducir a lenguaje m quina los programas escritos a en lenguaje ensamblador. La correspondencia entre ambos lenguajes es muy directa, por lo que los ensambladores suelen ser programas relativamente sencillos.

1.5.2 Int rpretes e


El objetivo de un int rprete es procesar una a una las instrucciones de un programa escrito en un lenguaje e de alto nivel. Para cada instrucci n se verica la sintaxis, se traduce a c digo m quina y nalmente se o o a ejecuta. Es decir, que la traducci n y la ejecuci n se realizan como una sola operaci n (ver Fig. 1.4). o o o

Los autores, 2000; Edicions UPC, 2000.

1.5. Traductores

ERRORES Edicin Compilacin Montaje Fuente Ejecucin Objeto Ejecutable

Figura 1.5: Fases en la compilaci n de un programa o La principal desventaja de los int rpretes es su lentitud para ejecutar los programas, pues es necee sario vericar la sintaxis y realizar la traducci n en cada ejecuci n. o o

1.5.3 Compiladores
La funci n de un compilador consiste en traducir un programa fuente escrito en un lenguaje de alto o nivel a su equivalente en c digo m quina (tambi n llamado c digo objeto). o a e o Mientras que un int rprete traduce y ejecuta al mismo tiempo cada una de las instrucciones, un e compilador analiza, traduce y posteriormente ejecuta todo el programa en fases completamente separadas (ver Fig. 1.5). As pues, una vez se ha compilado un programa, no es necesario volverlo a compilar cada vez. Esto hace que la ejecuci n de un programa compilado sea mucho m s r pida que la de uno o a a interpretado. El proceso de compilaci n o Edici n Consiste en escribir el programa fuente usando el lenguaje de programaci n seleccionado y su o o grabaci n en un chero. Para ello es necesario usar un programa editor, que puede o no formar o parte del entorno de desarrollo. Compilaci n En esta fase se verica la sintaxis del programa fuente y se traduce el programa a c digo o o m quina (objeto). Si se producen errores, el compilador muestra informaci n del tipo de error y a o d nde se ha producido. o Montaje Consistente en la combinaci n de los diferentes m dulos objeto y libreras del lenguaje para o o crear un programa ejecutable. Esta fase se conoce tambi n como linkado. e Ejecuci n En esta fase se invoca al programa de la manera adecuada dependiendo del sistema operativo o sobre el que vaya a funcionar. Como nota nal, cabe decir que todo lenguaje de programaci n puede ser tanto interpretado como o compilado. Sin embargo, dependiendo del las caractersticas del lenguaje y del uso mayoritario a que est destinado, es normal asociar a cada lenguaje una forma de traducci n particular. Por ejemplo, el e o lenguaje BASIC es mayoritariamente interpretado, mientras que C es compilado.

Los autores, 2000; Edicions UPC, 2000.

2. Primer contacto con C

Captulo 2

Primer contacto con C


2.1 Un poco de historia
El lenguaje de programaci n C fue desarrollado por Dennis Ritchie en los Laboratorios Bell de la o empresa de comunicaciones AT&T, en 1972. C fue creado inicialmente con un prop sito muy concreto: o el dise o del sistema operativo UNIX. Sin embargo, pronto se revel como un lenguaje muy potente y n o exible, lo que provoc que su uso se extendiese r pidamente, incluso fuera de los Laboratorios Bell. o a De esta forma, programadores de todo el mundo empezaron a usar el lenguaje C para escribir programas de todo tipo. Durante a os, el est ndar de facto del lenguaje C fue el denido en el libro El lenguaje de pron a gramaci n C, escrito por Brian Kernighan y Dennis Ritchie en 1978. Sin embargo, con el tiempo o proliferaron distintas versiones de C, que con sutiles diferencias entre ellas, empezaron a causar problemas de incompatibilidad a los programadores. Con el n de cambiar esta situaci n, el Instituto Nacional o de Est ndares Americano (m s conocido como ANSI) cre un comit en 1983 para establecer una dea a o e nici n est ndar de C que fuese no ambigua e independiente de la arquitectura interna de cualquier o a ordenador. Finalmente, en 1989 se estableci el est ndar ANSI C. Actualmente, cualquier compilador o a moderno soporta ANSI C. Sin embargo, probablemente debido a razones comerciales, la opci n por o defecto en muchos compiladores de C es una versi n propia del desarrollador del compilador. Dicha o version suele ser ligeramente diferente e incompatible con el est ndar ANSI C. a El lenguaje C debe su nombre a su predecesor, el lenguaje B desarrollado por Ken Thompson, tambi n en los Laboratorios Bell. e

2.2 Caractersticas del lenguaje


Actualmente existe gran variedad de lenguajes de programaci n de alto nivel entre los que elegir, como o BASIC, Pascal, C, C++, Java, etc. Todos ellos pueden usarse para resolver la mayora de proyectos de programaci n. Sin embargo, existen algunas razones que hacen de C el preferido de muchos programao dores: Potencia y exibilidad. Se ha usado en contextos tan dispares como el desarrollo de sistemas operativos, procesadores de texto, gr cos, bases de datos, compiladores de otros lenguajes, etc. a

Los autores, 2000; Edicions UPC, 2000.

2.3. Creaci n de un programa o

Popularidad. Existe una gran variedad de compiladores, libreras, herramientas de apoyo a la programaci n, etc. Es el lenguaje predominante en el entorno UNIX. o Portabilidad. El mismo programa escrito en C puede compilarse y ejecutarse sin pr cticamente a ning n cambio en diferentes ordenadores. Esto se debe en gran parte al est ndar ANSI C. u a Sencillez. C utiliza pocas palabras clave, por lo que puede aprenderse f cilmente. a Estructura y modularidad. Los programas en C pueden escribirse agrupando el c digo en funcioo nes que a su vez se agrupan en distintos m dulos. De esta forma, el c digo puede reutilizarse. o o De acuerdo con esto, C representa una buena elecci n como lenguaje de programaci n. Sin emo o bargo, seguro que el lector ha odo hablar de los lenguajes C++, Java y de la programaci n orientada a o objetos, adem s de preguntarse sobre las diferencias entre C y C++. Pues bien, C++ puede verse como a un superconjunto de C, lo que signica que casi cualquier aspecto de C es perfectamente v lido en C++ a (pero no al rev s). Java por su parte, al igual que C++, tambi n se basa en la sintaxis de C. e e Finalmente, diremos que aunque C es considerado como un lenguaje de alto nivel, mantiene muchas caractersticas de los lenguajes de bajo nivel, por lo que podra clasicarse como de nivel bajo-medio.

2.3 Creaci n de un programa o


La creaci n de un programa de ordenador consta generalmente de una serie de pasos claramente difeo renciados.

Edici n o
El primer paso consiste en usar un editor de textos y crear un chero que contenga el c digo del o programa en C. Este c digo, normalmente llamado c digo fuente, servir para dar instrucciones precisas o o a al ordenador. Por ejemplo, la siguiente linea de c digo fuente en C indica al ordenador que debe mostrar o el mensaje entre comillas en la pantalla: printf( "Esto es un mensaje" ); El formato del texto admitido por la mayora de compiladores se basa en el C digo Est ndar Ameri o a cano para el Intercambio de Informaci n (ASCII). La mayor parte de los procesadores de texto utilizan o c digos especiales para dar formato a los documentos, por lo que normalmente no pueden ser usados o como editores de programas. Hoy en da, la mayora de los entornos de programaci n incluyen un editor, sin embargo, otros no. o En estos casos pueden usarse otros programas gen ricos de edici n de textos ASCII proporcionados por e o el sistema. Por ejemplo, en UNIX pueden usarse editores como ed, ex, edit, vi, emacs, o nedit, entre otros. En MS-Windows puede usarse el Bloc de Notas. En MS-DOS puede usarse edit. En OS/2, pueden usarse E y EPM. El chero fuente de un programa debe grabarse con un nombre. Normalmente, el nombre del chero debe permitir intuir qu hace el programa. Adicionalmente, los cheros fuente en C suelen e tener la extensi n .c para identicarlos f cilmente. o a

Los autores, 2000; Edicions UPC, 2000.

2. Primer contacto con C

Compilaci n o
Puesto que el ordenador es incapaz de entender directamente un lenguaje de alto nivel como C, antes de que un programa pueda ejecutarse en el ordenador debe traducirse a lenguaje m quina. Esta traducci n a o la realiza un programa llamado compilador que, dado un chero fuente, produce un chero con las instrucciones de lenguaje m quina correspondientes al programa fuente original. El nuevo chero a recibe el nombre de chero objeto. El chero objeto suele tener el mismo nombre que el chero fuente, pero con la extensi n .OBJ (o o .o en UNIX).

Montaje
En el tercer paso, las diferentes partes del c digo compilado se combinan para crear el programa ejecuo table. Parte del lenguaje C consiste en una librera de funciones precompiladas que contiene c digo objeto. o Las funciones en esta librera realizan operaciones de uso frecuente, como mostrar datos en pantalla o leer datos de un chero. La funci n printf del ejemplo anterior es una funci n de dicha librera. As o o pues, el chero objeto producido al compilar el chero fuente debe combinarse con el c digo objeto de o la librera para crear el chero del programa ejecutable. Cabe destacar que en la mayora de compiladores actuales, como los que funcionan en MS-DOS o MS-Windows, compilaci n y montaje se realizan como si fuesen una sola acci n. o o

2.4 Primeros pasos con C


A continuaci n se muestra el programa m s sencillo posible en C: o a void main()

f g

Todo programa en C debe tener una y s lo una funci n main(). Esta funci n deber constar de una o o o a serie de sentencias (en este caso vaca) delimitada por los smbolos f g. Dichas sentencias especican la secuencia de acciones que el programa deber llevar a cabo. a En C pueden ponerse comentarios en cualquier lugar del programa, utilizando los smbolos /* */. El compilador de C ignora todo el texto entre el inicio del comentario (/*) y el nal del mismo (*/). A adir comentarios a un programa en C no incrementa el tama o de los cheros objeto ni ejecutable, n n ni tampoco ralentiza la ejecuci n del programa. Veamos un ejemplo de programa con comentarios: o /* Mi primer programa en C */ void main()

f g

/* Otro comentario */ y otro comentario */

/* ...

Sin embargo, no es posible poner un comentario dentro de otro. Por ejemplo sera ilegal:

Los autores, 2000; Edicions UPC, 2000.

2.5. El modelo de compilaci n de C o

10

/* Mi primer programa en C */ void main()

f g

/* Otro comentario /* Comentario ilegal */ */ y otro comentario */

/* ...

Pero veamos un programa no tan simple. Por ejemplo, el siguiente programa usa la funci n o printf, predenida en la librera est ndar stdio.h, para mostrar un mensaje en la pantalla. a /* Mi primer programa en C */ #include <stdio.h> void main()

f g

printf( "Mi primer mensaje en pantalla

nn"

);

2.5 El modelo de compilaci n de C o


A continuaci n se describe el modelo de compilaci n de C y los distintos procesos implicados: preproo o cesador, compilador y montador (ver Fig. 2.1).

Preprocesador
Aunque en el ap ndice A se ver en detalle esta parte del proceso de compilaci n, seguidamente se e a o describen algunos aspectos b sicos. a El preprocesador toma como entrada el c digo fuente y es el responsable de eliminar los comeno tarios (ya que en realidad no representan ninguna instrucci n) y de interpretar las directivas especiales o del preprocesador, denotadas por el smbolo #. Por el momento destacaremos s lamente dos de las o directivas m s utilizadas: a #include, que incluye un chero externo dentro del chero fuente. Se usar n los smbolos a < > para indicar que el chero se encuentra en un directorio del entorno de compilaci n, difeo rente del directorio de trabajo actual. Por el contrario, se usar n los smbolos " " para indicar a chero locales. Por ejemplo: #include <math.h> incluye el chero con las deniciones de las funciones matem ticas de la librera est ndar. a a #include <stdio.h> incluye el chero con las deniciones de las funciones de entrada y salida de la librera est ndar. a #include "funciones.h" incluye el chero funciones.h del directorio actual. #define, que dene un nombre simb lico. Cuando el preprocesador encuentra un nombre o simb lico en el programa lo substituye por el valor que se le haya asociado con la directiva o #define. #define NUM ELEMENTOS 100 dene la constante NUM ELEMENTOS con valor 100. #define PI 3.1416 dene la constante PI.

Los autores, 2000; Edicions UPC, 2000.

11

2. Primer contacto con C

Cdigo fuente

Preprocesador

Compilador Cdigo objeto Libreras Montador Cdigo ejecutable

Figura 2.1: Modelo de compilaci n de C o

Compilador
El compilador de C recibe el c digo fuente producido por el preprocesador y lo traduce a c digo objeto o o (cheros con extensi n .OBJ en MS-Windows, o extensi n .o en UNIX). o o

Montador
Si un chero fuente hace referencia a funciones de una librera (como la librera est ndar) o a funciones a denidas en otros cheros fuente, el montador se encarga de: combinar todos los cheros objeto correspondientes, vericar que s lo uno de ellos contenga la funci n principal main() y o o crear el chero nalmente ejecutable.

Los autores, 2000; Edicions UPC, 2000.

2.5. El modelo de compilaci n de C o

12

Los autores, 2000; Edicions UPC, 2000.

13

3. Empezando a programar

Captulo 3

Empezando a programar
3.1 Identicadores
Un identicador en un lenguaje de programaci n es un nombre utilizado para referir un valor constante, o una variable, una estructura de datos compleja, o una funci n, dentro de un programa. Todo identicao dor est formado por una secuencia de letras, n meros y caracteres de subrayado, con la restricci n de a u o que siempre debe comenzar por una letra o un subrayado y que no puede contener espacios en blanco. Cada compilador ja un m ximo para la longitud de los identicadores, siendo habitual un m ximo de a a 32 caracteres. C diferencia entre may sculas y min sculas, seg n lo cual C considerar los identicadores contador, u u u a Contador y CONTADOR, por ejemplo, como diferentes. En cualquier caso, nunca pueden utilizarse las palabras reservadas del lenguaje para la construcci n o de identicadores. De acuerdo con el est ndar ANSI, C consta unicamente de 32 palabras reservadas a (ver Tab. 3.1). Tabla 3.1: Palabras reservadas de C auto break case char const continue default do double else enum extern oat for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

3.2 Estructura de un programa


Todo programa en C consta b sicamente de los siguientes elementos: a Directivas del preprocesador Deniciones de tipos de datos Declaraciones de funciones

Los autores, 2000; Edicions UPC, 2000.

3.3. Variables y constantes

14

Por el momento se tratar unicamente la declaraci n de la funci n main() que corresponde al a o o programa principal. La ejecuci n de un programa escrito en C siempre comienza por dicha funci n. o o Por esta raz n, en un programa s lo puede haber una funci n con dicho nombre. La denici n de tipos o o o o de datos se deja para el captulo 8. Toda funci n en C, y en particular la funci n main(), tiene la siguiente estructura: o o tipo datos nombre funcin ( parmetros ) o a

f g

variables locales; secuencia de sentencias;

No entraremos aqu en las particularidades de las funciones como el paso de par metros y la de a voluci n de resultados de un tipo de datos determinado (ver Cap. 10). Comentaremos simplemente o que tanto la devoluci n de resultados como los par metros son opcionales, y que en la mayora de o a programas sencillos no se usan en la denici n del programa principal. o A continuaci n se muestra, como ejemplo, un programa para evaluar la expresi n 3 o o /* Evaluando una expresin */ o #include <stdio.h> void main()
5

; =

32 4

int a, b, c = 5; a = 3 * c; b = 32 / 4; c = a - b; printf( "El valor de la expresin es: o

%dnn", c );

El cuerpo del programa principal lo constituyen todas las lneas de programa comprendidas entre a los smbolos f y g. En cada una de dichas lneas puede haber una o m s sentencias. Una sentencia es una orden completa para el ordenador. Toda sentencia debe acabar con un punto y coma (;).

3.3 Variables y constantes


Los programas de ordenador utilizan diferentes tipos de datos, por lo que requieren de alg n mecanismo u para almacenar el conjunto de valores usado. C ofrece dos posibilidades: variables y constantes. Una variable es un objeto donde se guarda un valor, el cual puede ser consultado y modicado durante la ejecuci n del programa. Por el contrario, una constante tiene un valor jo que no puede ser modicado. o

3.3.1 Variables
Toda variable debe declararse antes de ser usada por primera vez en el programa. Las sentencias de declaraci n de variables indican al compilador que debe reservar cierto espacio en la memoria del oro denador con el n de almacenar un dato de tipo elemental o estructurado. Por ejemplo, la siguiente

Los autores, 2000; Edicions UPC, 2000.

15

3. Empezando a programar

declaraci n de variables indica al compilador que debe reservar espacio en la memoria para tres variao bles de tipo entero, a las que nos referiremos con los nombres a, b y c: int a, b, c; La declaraci n consiste en dar un nombre signicativo a la variable e indicar el tipo de datos a que o corresponden los valores que almacenar . A continuaci n se muestra la sintaxis m s sencilla de una a o a sentencia de declaraci n para una sola variable. o tipo datos nombre variable; Adem s, en una sola sentencia pueden declararse varias variables de un mismo tipo de datos, sepaa rando los nombres de las variables mediante comas: tipo datos nombre variable1, ..., nombre variableN; Opcionalmente, es posible asignar un valor inicial a las variables en la propia declaraci n. o tipo datos nombre variable = valor inicial;

3.3.2 Constantes
C admite dos tipos diferentes de constantes: literales y simb licas. o Constantes literales Todo valor que aparece directamente en el c digo fuente cada vez que es necesario para una operaci n o o constituye una constante literal. En el siguiente ejemplo, los valores 20 y 3 son constantes literales del tipo de datos entero: int cont = 20; cont = cont + 3; Si una constante num rica contiene un punto decimal, el compilador considera dicha constante e como un valor real de coma otante. Este tipo de constantes puede escribirse tambi n utilizando alguna e de las notaciones cientcas com nmente aceptadas (ver Sec. 6.3). u Por el contrario, el resto de constantes num ricas son consideradas por el compilador, como valores e enteros. Pueden usarse tres formatos alternativos: Toda constante que comience por un dgito distinto de 0 es interpretada como un entero decimal (esto es, en base 10). Se especican mediante los dgitos del 0 al 9 y el signo positivo o negativo. Si una constante comienza con el dgito 0, se interpreta como un entero octal (base 8). Se especican mediante los dgitos del 0 al 7 y el signo positivo o negativo. Finalmente, las constantes que comienzan por 0x o 0X se interpretan como enteros en base hexadecimal (base 16). Se especican mediante los dgitos del 0 al 9, las letras de la A a la F, y el signo positivo o negativo. Para saber m s sobre los distintos sistemas de numeraci n, ver el ap ndice C. a o e

Los autores, 2000; Edicions UPC, 2000.

3.3. Variables y constantes

16

Constantes simb licas o Una constante simb lica es una constante representada mediante un nombre (smbolo) en el programa. o Al igual que las constantes literales, no pueden cambiar su valor. Sin embargo para usar el valor constante, se utiliza su nombre simb lico, de la misma forma que lo haramos con una variable. Una o constante simb lica se declara una sola vez, indicando el nombre y el valor que representa. o Las constantes simb licas tienen dos ventajas claras respecto a las literales. Supongamos el siguieno te c digo para calcular el permetro de una circunferencia y el area del crculo que dene: o perimetro = 2 * 3.14 * radio; area = 3.14 * radio * radio; Si por el contrario se hubiese denido una constante simb lica de nombre PI y valor 3.14, podramos o escribir un c digo mucho m s claro: o a perimetro = 2 * PI * radio; area = PI * radio * radio; Es m s, imaginemos ahora que para incrementar la precisi n del c lculo se desea usar un valor m s a o a a preciso de la constante , como 3.14159. En el primer caso debera substituirse uno a uno el valor 3.14 en todo el programa. En el segundo caso, bastara cambiar la denici n de la constante PI con o el nuevo valor. El m todo m s habitual para denir constantes en C es la directiva del preprocesador #define. e a Por ejemplo, en el caso anterior podramos haber escrito: #define PI 3.14159 Es decir, el nombre simb lico y a continuaci n el valor constante que representa. o o

3.3.3 Entrada y salida de valores


Aunque en el ap ndice B se ver n con m s detalle las funciones de la librera est ndar printf y e a a a scanf, se introducen en este punto con el n de poder realizar algunos programas sencillos. C utiliza operaciones de entrada y salida con formato. Por ejemplo, la funci n printf usa como o car cter especial de formato el smbolo de porcentaje (%). El car cter que sigue a este smbolo dene el a a formato de un valor (constante, variable o expresi n). Por ejemplo, %c para valores de tipo car cter o o a %d para valores de tipo entero. El siguiente ejemplo muestra por pantalla el contenido de una variable de tipo car cter (ch), y una variable entera (num). a char ch; int num; . . . printf( "Esto es un carcter: %cnn", ch ); a printf( "Y esto un entero: %dnn", num ); El formato es todo aquello especicado entre las comillas, al que le sigue una lista de variables, constantes o expresiones separadas por comas. Es responsabilidad del programador asegurar la perfecta

Los autores, 2000; Edicions UPC, 2000.

17

3. Empezando a programar

Tabla 3.2: Operadores aritm ticos en C e Unarios Signo negativo Incremento Decremento Suma Resta Multiplicaci n o Divisi n o M dulo o

;
++

;; ;
=
% +

Binarios

correspondencia entre el formato y la lista de valores, tanto en n mero como en el tipo de los mismos. u Finalmente, la secuencia especial nn indica un salto de lnea. Por su parte, scanf es una funci n para la entrada de valores a una estructura de datos, y en o particular a una variable. Su formato es similar al de printf. Por ejemplo: char ch; int num; . . . scanf( "%c%d", &ch, &num ); Permite introducir desde el teclado un car cter en la variable ch y seguidamente un valor entero en a la variable num. N tese que en el caso de scanf se antepone el smbolo & a las variables. Por o el momento, no debemos olvidar utilizarlo, y tengamos en mente que el uso de & tiene que ver con direcciones de memoria y punteros (ver Cap. 9).

3.4 Expresiones
Una expresi n es una f rmula matem tica cuya evaluaci n especica un valor. Los elementos que o o a o constituyen una expresi n son: constantes, variables y operadores. o

3.4.1 Operador de asignaci n o


El operador de asignaci n permite asignar valores a las variables. Su smbolo es un signo igual =. Este o operador asigna a la variable que est a la izquierda del operador el valor que est a la derecha. Un a a ejemplo de expresiones v lidas con el operador de asignaci n son: x = 1; z = 1.35; . a o

3.4.2 Operadores aritm ticos e


Adem s de los operadores aritm ticos tradicionales, C proporciona algunos operadores adicionales (ver a e Tab. 3.2). La expresi n, x++; equivale a x = x+1;, y x--; equivale a x = x-1;. Aunque en el o pasado algunos compiladores generaban c digo m s eciente si se usaba los operadores ++, -- en o a lugar de sus expresiones equivalentes, esto ya no es cierto en los compiladores modernos. Los operadores ++ y -- pueden usarse tanto de manera postja (m s habitual) como preja, indicando en cada a

Los autores, 2000; Edicions UPC, 2000.

3.4. Expresiones

18

caso si el valor de la variable se modica despu s o antes de la evaluaci n de la expresi n en la que e o o aparece. Por ejemplo, la siguiente lnea de c digo: o x = ((++z) - (w--)) % 100; es equivalente al siguiente grupo de sentencias: z = z + 1; x = (z - w) % 100; w = w - 1; N tese que en C no existe ning n operador especial para la divisi n entera, de forma que cuando o u o los dos operandos de la divisi n son enteros, el cociente que se obtiene es el correspondiente a la o divisi n entera (el cociente no se redondea, sino que se trunca). Si alguno de los operandos es un valor o real, el resultado de la divisi n ser tambi n real. Por ejemplo, x = 3/2; asigna el valor 1 a la o a e variable x (que debe ser entera), mientras que x = 3.0/2; o x = 3/2.0; asigna el valor 1.5 a la variable x (que debe ser real). Finalmente, el operador de m dulo (%) permite obtener o el resto de una divisi n entera, por lo que sus operandos deben ser tambi n enteros. Por ejemplo, o e x = 8 % 5; asigna el valor 3 a la variable entera x. Existe adem s una manera abreviada de expresar ciertos c lculos en C. Es muy com n tener exa a u presiones del estilo de i = i + 5; o x = x * (y + 2);. Este tipo de expresiones puede escribirse en C de forma compacta como: expresin1 o que es equivalente a: expresin1 = expresin1 o o op expresin2 o op = expresin2 o

Seg n esto, la asignaci n i = i + 5; puede reescribirse como i += 5; y la asignaci n u o o x = x * (y + 2); como x *= y + 2;. N tese que esta ultima expresi n no signica en o o ning n caso x = (x * y) + 2;. u Como puede verse, un uso abusivo de los operadores abreviados de C puede hacer difcil de leer un programa. C permite escribir expresiones muy compactas, pero que pueden generar confusi n al ser o ledas. Hay que recordar siempre que la legibilidad (mantenimiento) de un programa es tan importante como su correcto funcionamiento.

3.4.3 Operadores relacionales


Los operadores relacionales se utilizan principalmente para elaborar condiciones en las sentencias condicionales e iterativas (ver Cap. 4 y Cap. 5). La tabla 3.3 resume los distintos operadores de relaci n en C. Al relacionar (comparar) dos expreo siones mediante uno de estos operadores se obtiene un resultado l gico, es decir: CIERTO o FALSO. o Por ejemplo, la expresi n 4 > 8 da como resultado el valor falso, la expresi n num == num da o o como resultado cierto, la expresi n 8 <= 4 da como resultado falso, etc. o Es interesante destacar que a diferencia de otros lenguajes, C no dispone de un tipo de datos especco para los valores l gicos o booleanos. En su lugar, C representa un resultado FALSO como o

Los autores, 2000; Edicions UPC, 2000.

19

3. Empezando a programar

Tabla 3.3: Operadores relacionales (izquierda) y l gicos (derecha) en C o Menor que Mayor que Menor o igual que Mayor o igual que Igual que Distinto que

< > < >

= =

== ! =

Conjunci n o Y l gico o o Disyunci n u O l gico o o Negaci n o NO l gico o o

&&

jj
!

Tabla 3.4: Tabla de verdad de los operadores l gicos en C o A Cierto Cierto Falso Falso B Cierto Falso Cierto Falso !A Falso Falso Cierto Cierto A && B Cierto Falso Falso Falso A jj B Cierto Cierto Cierto Falso

el valor num rico entero cero, y un resultado CIERTO como cualquier valor entero diferente de cero. e Es muy importante recordar este hecho de ahora en adelante. Un error habitual es confundir el operador relacional de igualdad == con el operador de asignaci n o = . Por ejemplo, la sentencia x = 3 asigna el valor 3 a la variable x, mientras que x == 3 compara el valor de x con la constante 3.

3.4.4 Operadores l gicos o


Los operadores l gicos (ver Tab. 3.3) se utilizan principalmente en conjunci n con los relacionales para o o elaborar condiciones complejas en las sentencias condicionales e iterativas (ver Cap. 4 y Cap. 5). Es importante no confundir los operadores l gicos && y o o & y j que veremos en la secci n 8.5.

jj

con los operadores de manejo de bits

La tabla 3.4 muestra la tabla de verdad para los operadores l gicos. De acuerdo con dicha tabla, o las expresiones 4 && 0, !(4 > 1) y 5 <= 0 dan como resultado 0 (falso), mientras que las expresiones 4 jj 9, (8 == 4*2) && (5 > 2) y 2 && (4 < 9) dan como resultado 1 (cierto).

3.4.5 Prioridad de operadores


La tabla 3.5 muestra los operadores vistos anteriormente, as como la prioridad entre ellos. La prioridad desciende al descender en la tabla. Tambi n se muestra la asociatividad para operadores con el mismo e nivel de prioridad. Por ejemplo, seg n dicha tabla, la expresi n (a < 10 && 2 * b < c) se interpretar como u o a (a < 10) && ((2 * b) < c).

Los autores, 2000; Edicions UPC, 2000.

3.5. Ejercicios

20

Tabla 3.5: Prioridad y asociatividad de los operadores en C Operador Par ntesis e NO l gico o Signo negativo Incremento Decremento Multiplicaci n o Divisi n o M dulo o Suma Resta Menor que Menor o igual que Mayor que Mayor o igual que Igual que Distinto que Y l gico o O l gico o Asignaciones Smbolo
()

;
++

Asociatividad Izquierda a derecha Derecha a izquierda

;;
=
% Izquierda a derecha Izquierda a derecha Izquierda a derecha

< < > >

== ! =

Izquierda a derecha Izquierda a derecha Izquierda a derecha Derecha a izquierda

&&

jj

= =

+ =

%=

3.5 Ejercicios
Escribir un programa para cada uno de los siguientes ejercicios: 1. Pedir la base y la altura de un rect ngulo, calcular su area y su permetro, y mostrar los resultados a por pantalla. 2. Pedir una cantidad de segundos y mostrar por pantalla a cu ntas horas, minutos y segundos a corresponden. 3. Suponiendo que previamente se ha realizado la declaraci n int x = 7, y; , calcular el o valor de la variable y tras evaluar cada una de las siguientes sentencias de asignaci n: o (a) y (b) y (c) y (d) y
=

;2
(y

;;x;
x); x;

+ = = =

2;
==

y++

4. Evaluar las siguientes expresiones: (a) 5 (b) 4

2 6

20 % 6 2

15

Los autores, 2000; Edicions UPC, 2000.

21

3. Empezando a programar

(c) 5 (d) 8 (e) (4

15
==

= <

16 3

jj

2 6

(4 3

;
>

2) 5

7 != 4 && 4

jj

<

1
+

2) && 3

<

12

Los autores, 2000; Edicions UPC, 2000.

3.5. Ejercicios

22

Los autores, 2000; Edicions UPC, 2000.

23

4. Construcciones condicionales

Captulo 4

Construcciones condicionales
Una de las construcciones importantes que pueden especicarse en un programa es el hecho de realizar diferentes tareas en funci n de ciertas condiciones. Esto es, ejecutar una parte del c digo u otra, cono o dicionalmente. Para ello ser necesario especicar dichas condiciones (ver Sec. 3.4) y disponer de un a mecanismo para indicar qu acciones tomar dependiendo de c mo se eval e una determinada condici n e o u o en un momento dado de la ejecuci n del programa. o Antes de empezar, un recordatorio. Como ya de coment en la secci n 3.4.3, C no dispone de o o valores booleanos o l gicos, que podran usarse en la evaluaci n de condiciones. En su defecto, C o o simula los valores falso y cierto, como el valor num rico cero, y cualquier valor no cero (incluyendo e negativos), respectivamente. As pues, en este captulo veremos las distintas maneras que C ofrece para controlar el ujo de ejecuci n de un programa de forma condicional, que son: o la construcci n if, o el operador condicional ?, y la construcci n switch. o

4.1 Construcci n if o
La construcci n if es similar a la existente en otros lenguajes de programaci n, aunque en C poo o see ciertas peculiaridades. El formato general de esta construcci n para decidir si una determinada o sentencia debe ejecutarse o no (alternativa simple) es el siguiente: if (condicin) o sentencia; La construcci n if puede escribirse tambi n de forma m s general para controlar la ejecuci n de un o e a o grupo de sentencias, de la siguiente manera:

Los autores, 2000; Edicions UPC, 2000.

4.1. Construcci n if o

24

Cierto Condicin Grupo de sentencias

Cierto

Condicin

Falso

Grupo de sentencias 1

Grupo de sentencias 2

(a)

(b)

Figura 4.1: Esquema de funcionamiento de if y de if-else if (condicin) o

sentencia 1; sentencia 2; . . . sentencia N;

El funcionamiento de la construcci n if es muy simple. En primer lugar se eval a la condici n, o u o que no es otra cosa que una expresi n de tipo entero. A continuaci n, si la expresi n se ha evaluado o o o como cierta, se ejecuta la sentencia o grupo de sentencias. En caso contrario la ejecuci n del programa o contin a por la siguiente sentencia en orden secuencial (ver Fig. 4.1 (a)). u El siguiente ejemplo muestra el uso de la construcci n if. El programa lee un n mero entero y lo o u transforma en el impar inmediatamente mayor, si es que no era ya impar. #include <stdio.h> void main()

int a; scanf("%d", &a); if (a % 2 == 0) /* Comprobar si a es par. a = a + 1; printf( "Ahora es impar: %dnn", a );

*/

N tese que despu s de la condici n no se escribe ;. Escribir ; detr s de la condici n equivaldra o e o a o a que la construcci n if ejectutase un conjunto vaco de sentencias, lo cual no tiene ning n sentido. o u N tese, sin embargo, que tal hecho es v lido sint cticamente (no produce ning n error de compilaci n), o a a u o por lo que deber tenerse cuidado al escribir esta construcci n. Algo similar ocurre con los bucles a o for y while (ver Cap. 5).

Los autores, 2000; Edicions UPC, 2000.

25

4. Construcciones condicionales

4.1.1 Variante if-else


Existe otra forma m s general, denominada alternativa doble, que ofrece dos alternativas de ejecuci n, a o en funci n de si la condici n se eval a cierta o falsa. o o u if (condicin) o sentencia 1; else sentencia 2; Y para un grupo de sentencias: if (condicin) o

f g f g

grupo de sentencias 1;

else grupo de sentencias 2;

As pues, si la condici n es cierta se ejecutar la primera sentencia (el primer grupo de sentencias), y si o a es falsa se ejecutar la segunda sentencia (el segundo grupo). Ver gura 4.1 (b). a El siguiente programa muestra el uso de esta construcci n. El programa calcula el m ximo de dos o a n meros enteros: u #include <stdio.h> void main() int a, b, max; scanf( "%d %d", &a, &b ); if (a > b) max = a; else max = b; prinf( "El mximo es: %dnn", max ); a

Es importante destacar que la sentencia en la construcci n else es opcional, es decir, puede ser o nula. Ve moslo en el siguiente ejemplo que determina si un n mero es par: a u #include <stdio.h> void main()

int x;

Los autores, 2000; Edicions UPC, 2000.

4.1. Construcci n if o

26

scanf( "%d", &x ); if (x % 2 == 0) printf( "Es par.nn" ); else ;

El hecho de que la construcci n else sea opcional puede causar problemas de ambig edad al o u compilador cuando se utilizan construcciones if o if-else anidadas. Para solventar el problema se ha establecido una regla muy sencilla que todo compilador de C tiene en cuenta. La regla consiste en que una sentencia else se asocia con el if precedente m s cercano siempre y cuando este no a tenga ya asociada otra sentencia else. A continuaci n se muestran dos porciones de programa pr cticamente iguales, pero con comportao a mientos completamente diferentes. Se deja para el lector el an lisis de ambos casos. a . . . if (n > 0) if (a > b) z = a; else z = b; . . . . . . if (n > 0)

f g

if (a > b) z = a;

else z = b; . . .

4.1.2 Variante if-else-if


Existe nalmente otra construcci n alternativa muy com n, conocida como if-else-if o simpleo u mente else-if. Su construcci n, donde las condiciones se plantean de forma escalonada, se muestra o a continuaci n: o if (condicin 1) o

f g f g f g f g

grupo de sentencias 1;

else if (condicin 2) o grupo de sentencias 2;

. . . else if (condicin N) o grupo de sentencias N;

else grupo de sentencias por defecto;

Los autores, 2000; Edicions UPC, 2000.

27

4. Construcciones condicionales

Las condiciones se eval an secuencialmente de arriba hacia abajo hasta encontrar una que d como u e resultado cierto. En ese punto, se ejecuta el grupo de sentencias correspondiente a dicha condici n. El o resto de condiciones y sentencias asociadas se ignoran. En caso de que ninguna de las condiciones se eval e cierta, se ejecutara el grupo de sentencias por defecto. Como en todos los casos anteriores, el u ultimo else es opcional. A continuaci n se muestra un ejemplo del uso de esta construcci n: o o #include <stdio.h> void main()

int hora; scanf( "%d", &hora ); if ((hora >= 0) && (hora < 12)) printf( "Buenos das" ); else if ((hora >= 12) && (hora < 18)) printf( "Buenas tardes" ); else if ((hora >= 18) && (hora < 24)) printf( "Buenas noches" ); else printf( "Hora no vlida" ); a

4.2 El operador condicional ?


El operador condicional ? es el unico operador ternario de C. La forma general de las expresiones construidas con este operador es la siguiente: expresin 1 ? o expresin 2 : o expresin 3; o

De manera que si la primera expresi n se eval a cierta, toda la expresi n toma el valor de la segunda o u o expresi n. En cambio, si la primera expresi n se eval a falsa, toda la expresi n toma el valor de la o o u o tercera expresi n. o Un ejemplo tpico del uso de este operador es el c lculo del m ximo de dos valores. En la siguiente a a sentencia, c toma el valor del m ximo entre la variable a y b. a c = (a > b) ? a : b;

Esto mismo podra haberse escrito usando la construcci n if-else como: o if (a > b) c = a; else c = b;

Los autores, 2000; Edicions UPC, 2000.

4.3. Construcci n switch o

28

De esta manera, algunas sentencias if-else sencillas pueden escribirse de manera muy compacta mediante el operador ?. Finalmente, el operador condicional, por ser en realidad un operador para expresiones, puede usarse en lugares donde no puede usarse un if-else, como se muestra a continuaci n: o printf("El mnimo es %d

nn",

((x < y) ?

x :

y) );

4.3 Construcci n switch o


Esta construcci n permite especicar m ltiples sentencias al estilo if-else-if, pero de manera o u m s compacta, legible y elegante. Su forma general es la siguiente: a switch ( expresin ) o

case constante 1 : grupo de sentencias break; case constante 2 : grupo de sentencias break; . . . case constante N : grupo de sentencias break; default : grupo de sentencias break;

1;

2;

N;

por defecto;

donde la expresi n debe ser de tipo entero o car cter, al igual que todas las constantes asociadas a cada o a etiqueta case. Es importante resaltar que no pueden usarse variables o expresiones en los distintos case, sino s lo constantes. o El funcionamiento de la construcci n switch es como sigue. En primer lugar se eval a la o u expresi n. Seguidamente su valor es comparado secuencialmente con el de las diferentes constantes en o los case. Si el valor de la expresi n coincide con alguna de ellas, se ejecuta el grupo de sentencias o correspondiente y switch concluye gracias a la sentencia break. En caso contrario, y si existe el caso default (que es opcional), se ejecutara el grupo de sentencias por defecto (ver Fig. 4.2). Cabe mencionar de forma especial, la sentencia break que volveremos a ver en captulos sucesi vos. En general, break se utiliza para nalizar de forma forzada la ejecuci n dentro de un bloque de o c digo, de manera que la siguiente sentencia a ejecutar ser la primera sentencia justo despu s de dicho o a e bloque. En la construcci n switch, break es necesario para concluir la ejecuci n del grupo de o o sentencias asociado al caso cuya constante coincide con el valor de la expresi n. As pues, la sentencia o a ejecutar despu s de break en un switch, ser la primera sentencia posterior a la llave g que e a cierra el switch.

Los autores, 2000; Edicions UPC, 2000.

29

4. Construcciones condicionales

Expresin

Grupo de sentencias 1

Grupo de sentencias 2

...

Grupo de sentencias N

Grupo de sentencias por defecto

Figura 4.2: Esquema de funcionamiento de switch La construcci n switch tambi n podra escribirse de forma equivalente mediante sentencias del o e tipo if-else-if, de la siguiente forma: if (expresin == constante 1) o

f g f f g f g

grupo de sentencias 1;

else if (expresin == constante 2) o grupo de sentencias 2; g. . . else if (expresin == constante N) o grupo de sentencias N;

else grupo de sentencias por defecto;

que, como puede verse, es mucho m s ineciente en tiempo de ejecuci n, puesto que la expresi n debe a o o evaluarse repetidas veces, una para cada condici n. o El siguiente ejemplo muestra un programa que hace uso de switch para traducir a caracteres un dgito entre 1 y 5. #include <stdio.h> void main() int num; scanf( "%d", &num ); switch ( num )

Los autores, 2000; Edicions UPC, 2000.

4.3. Construcci n switch o

30

f
case 1 : printf( "Uno.nn" ); break; case 2 : printf( "Dos.nn" ); break; . . . case 5 : printf( "Cinco.nn" ); break; default : printf( "El dgito est fuera de rango.nn" ); a break;

Finalmente, cabe decir que el grupo de sentencias asociado a un case puede ser vaco. Este caso particular tiene su utilidad cuando se desea que varias etiquetas case ejecuten un mismo grupo de sentencias. Por ejemplo: #include <stdio.h> void main() int num; scanf( "%d", &num ); switch ( num )

case 1: case 3: case 7: printf( "Es un uno, un tres o un siete.nn" ); break; case 4: case 8: printf( "Es un cuatro, o un ocho.nn" ); break; default: printf( "Dgito no controlado.nn" ); break;

Los autores, 2000; Edicions UPC, 2000.

31

4. Construcciones condicionales

4.4 Ejercicios
1. Escribir un programa que lea tres valores enteros y muestre por pantalla el m ximo y el mnimo a de ellos. 2. Dado el siguiente programa, realizar un seguimiento de la ejecuci n en los siguientes supuestos: o (a) a = 0, b = 0, c = 5, d = 3 (b) a = 2, b = 1, c = 5, d = 3 (c) a = 2, b = 1, c = 2, d = 2 (d) a = 2, b = 1, c = 0, d = 0 #include <stdio.h> void main() int a, b, c, d; scanf( "%d %d %d %d", &a, &b, &c, &d ); if ( ((a > 0) || (b > a)) && (c != d) )

f g f g g

a = c; b = 0;

else c += d; c = (c == 0) ? b = a + c + d;

(c + b) :

(c - a);

printf( "%d %d %d %dnn", a, b, c, d );

3. Escribir un programa que lea un valor entero y determine si es m ltiplo de 2 y de 5. u 4. Escribir un programa que muestre por pantalla la ecuaci n de una recta en un plano, Ax + By + o C = 0, leyendo previamente las coordenadas de dos de sus puntos (x1 , y1) y (x2 , y2). Recordar que:

A y ;y
=
2

y B y
=

x ;x ;x
2 1)

y ;y
2

1)

Los autores, 2000; Edicions UPC, 2000.

4.4. Ejercicios

32

Los autores, 2000; Edicions UPC, 2000.

33

5. Construcciones iterativas

Captulo 5

Construcciones iterativas
Hasta ahora hemos visto algunos aspectos b sicos del control del ujo de ejecuci n de un programa en a o C. Este captulo presenta los mecanismos que C ofrece para la ejecuci n repetida de sentencias, bien o un n mero prejado de veces, bien dependiendo de cierta condici n. Es decir, mecanismos para la u o creaci n de bucles de ejecuci n. o o C proporciona las siguientes construcciones iterativas: la construcci n while, o la construcci n do-while, y o la construcci n for. o

5.1 Construcci n while o


La construcci n while es similar a la existente en otros lenguajes de programaci n. Sin embargo, o o debido a que en C toda sentencia puede considerarse como una expresi n, la construcci n while de o o C ofrece cierta potencia a adida. n La forma m s general para la ejecuci n repetida de una sola sentencia es: a o while (condicin) o sentencia; O para la ejecuci n repetida de un grupo de sentencias: o while (condicin) o

f g

grupo de sentencias;

El funcionamiento de esta construcci n es bastante simple. El cuerpo del bucle, es decir, la seno tencia o grupo de sentencias dentro del bucle, se ejecuta mientras el valor de la expresi n que act a de o u condici n sea cierto. En el momento en que la condici n sea falsa, la ejecuci n del programa contin a o o o u secuencialmente con la siguiente instrucci n tras el bucle (ver Fig. 5.1). o

Los autores, 2000; Edicions UPC, 2000.

5.1. Construcci n while o

34

Falso Condicin Cierto Grupo de sentencias

Figura 5.1: Esquema de funcionamiento de while El siguiente ejemplo calcula la media de una secuencia de n meros enteros ledos por teclado acau bada en ;1: #include <stdio.h> void main()

int num, cont, suma; cont = 0; suma = 0; scanf( "%d", &num ); while (num != -1)

f g

cont++; suma = suma + num; scanf( "%d", &num );

if (cont != 0) printf( "La media es %dnn", sum/cont ); else printf( "La secuencia es vaca.nn" );

En la construcci n while la condici n se eval a al principio del bucle. Por ello, si cuando se alo o u canza el bucle por primera vez, la condici n es falsa, el cuerpo del bucle no se ejecuta nunca (imagnese o el caso, en el ejemplo anterior, en que el primer n mero de la secuencia sea ;1). Como consecuencia, u o el cuerpo de un bucle while puede ejecutarse entre 0 y N veces, donde N depende de la condici n. N tese tambi n que si la condici n permanece cierta, el bucle puede no terminar nunca (imagnese o e o qu ocurrira si se elimina la sentencia scanf del cuerpo del bucle del ejemplo anterior). Por ello, e habitualmente las sentencias del cuerpo del bucle modican las variables que aparecen en la condici n, o de forma que esta sea falsa en alg n momento. u Por otra parte, la condici n del bucle (y esto es extensible a las diferentes construcciones repetitivas) o

Los autores, 2000; Edicions UPC, 2000.

35

5. Construcciones iterativas

Grupo de sentencias

Condicin Falso

Cierto

Figura 5.2: Esquema de funcionamiento de do-while no tiene por qu ser simplemente una expresi n l gica, sino que puede ser cualquier expresi n. Por e o o o ejemplo, los siguiente bucles while (x--)

...

while (x = x+1); son perfectamente v lidos. En ambos casos el cuerpo del bucle se repetir mientras el valor de x sea a a o e distinto de 0. N tese que en el segundo caso el cuerpo del bucle es nulo, lo cual tambi n es posible.

5.2 Construcci n do-while o


La forma general de la construcci n do-while es la siguiente: o do

f g

sentencia; o grupo de sentencias; while (condicin); o

N tese que tanto para ejecutar una sola sentencia como para ejecutar un grupo de ellas, las llaves o f g son igualmente necesarias. Esta construcci n funciona de manera muy similar a la construcci n while. Sin embargo, al o o contrario que esta, do-while ejecuta primero el cuerpo del bucle y despu s eval a la condici n. Por e u o lo cual, el cuerpo del bucle se ejecuta como mnimo 1 vez (ver Fig. 5.2). El siguiente ejemplo cuenta el n mero de veces que aparece el n mero u u n meros enteros acabada en ;1: u #include <stdio.h> void main() int num, cont;
3

en una secuencia de

Los autores, 2000; Edicions UPC, 2000.

5.3. Construcci n for o

36

Sentencia inicial

Falso

Condicin Cierto

Grupo de sentencias

Incremento / Decremento

Figura 5.3: Esquema de funcionamiento de for cont = 0;

do

scanf( "%d", &num ); if (num == 3) cont++; g while (num != -1); printf( "El 3 ha aparecido %d vecesnn", cont );

Es importante destacar el uso de ; despu s de la condici n, a diferencia de en la construcci n e o o while , donde no se utiliza. Finalmente, cabe decir que tradicionalmente, tanto la construcci n while como la construcci n o o do-while se utilizan en bucles donde se desconoce a priori el n mero exacto de iteraciones. u

5.3 Construcci n for o


Esta construcci n iterativa no presenta un formato jo estricto, sino que admite numerosas variantes, lo o que la dota de gran potencia y exibilidad. Su forma m s general para la ejecuci n repetida de una sola sentencia es: a o for (sentencia inicial ; condicin ; incremento/decremento) o sentencia; o para la ejecuci n repetida de un grupo de sentencias: o

Los autores, 2000; Edicions UPC, 2000.

37

5. Construcciones iterativas

for (sentencia inicial ; condicin ; incremento/decremento) o

f g

grupo de sentencias;

La primera parte de la construcci n for acostumbra a ser una sentencia de asignaci n donde se o o inicializa alguna variable que controla el n mero de veces que debe ejecutarse el cuerpo del bucle. Esta u sentencia se ejecuta una sola ocasi n, antes de entrar por primera vez al cuerpo del bucle. o La segunda parte corresponde a la condici n que indica cu ndo naliza el bucle, de la misma forma o a que en las construcciones iterativas anteriores. En este caso, la condici n se eval a antes de ejecutar el o u cuerpo del bucle, por lo que al igual que en la construcci n while, el cuerpo puede ejecutarse entre 0 o y N veces, donde N depende de la condici n. o La tercera parte corresponde normalmente a una sentencia de incremento o decremento sobre la variable de control del bucle. Esta sentencia se ejecuta siempre despu s de la ejecuci n del cuerpo del e o bucle. La gura 5.3 muestra esquem ticamente el funcionamiento del bucle for. a El programa del siguiente ejemplo utiliza la construcci n for para calcular el sumatorio o #include <stdio.h> void main()

X
10

=1

int i, cubo, suma; suma = 0; for (i = 0 ; i <= 10 ; i++)

f g g

cubo = i * i * i; suma += cubo; printf( "El sumatorio es %dnn", suma );

Las tres partes de la construcci n for son opcionales, por lo que es posible omitir alguna o todas o ellas. En cualquier caso, los punto y coma (;) separadores son siempre necesarios. Un ejemplo cl sico a de este tipo de bucle for es el bucle innito (nunca concluye la ejecuci n): o for ( ; 1 ; )

f g

/* Grupo de sentencias */

Tradicionalmente la construcci n for se utiliza en bucles donde el n mero exacto de iteraciones o u es conocido a priori, y puede controlarse mediante una variable que act a como contador. u

Los autores, 2000; Edicions UPC, 2000.

5.4. Las sentencias break y continue

38

5.3.1 El operador coma (,)


C permite la utilizaci n de m s de una sentencia en la primera y tercera partes de la construcci n for, o a o as como de m s de una condici n en la segunda parte. Por ejemplo, el siguiente bucle es v lido en C: a o a for (i = 0, j = 10 ; i < 10, j > 0 ; i++, j-=2)

f g

/* Grupo de sentencias */

As pues, las variables i y j se inicializan a 0 y 10, respectivamente, antes de comenzar la ejecuci n o del bucle. En la segunda parte de la construcci n, aparecen dos condiciones, i < 10 y j > 0. Si o alguna de ellas es falsa, la ejecuci n del bucle se detiene. Finalmente, tras ejecutarse el cuerpo del bucle, o i se incrementa en 1 y j se decrementa en 2, tras lo cual vuelven a comprobarse las condiciones, y as sucesivamente.

5.3.2 Equivalencia for-while


Como se ha podido ver, C trata el bucle for de manera muy similar al bucle while, usando una condici n para decidir cu ndo concluye la ejecuci n. De hecho, todo bucle for puede escribirse de o a o forma equivalente mediante un bucle while de la siguiente forma: sentencia inicial; while (condicin) o

f g

sentencia; incremento/decremento;

5.4 Las sentencias break y continue


C proporciona dos mecanismos para alterar la ejecuci n de las construcciones iterativas: las sentencias o break y continue.

break
Esta sentencia tiene una doble nalidad. Por un lado, indica el nal de un case en la construcci n o switch, como ya se vi en la secci n 4.3. Y por otro, para forzar la terminaci n inmediata de la ejecuo o o ci n de un bucle. De esta forma, se permite salir de la construcci n repetitiva ignorando la evaluaci n o o o de la condici n. Si bien su uso est reconocido como no muy elegante, permite en ocasiones escribir o a programas m s legibles y compactos. a

continue
Esta sentencia se utiliza unicamente en las construcciones repetitivas. Su funci n es la de evitar que se o ejecute todo el c digo a continuaci n de ella y hasta el nal del cuerpo del bucle, durante una iteraci n o o o determinada. El siguiente ejemplo pretende ilustrar el uso de estas sentencias:

Los autores, 2000; Edicions UPC, 2000.

39

5. Construcciones iterativas

do

scanf("%d", &num); if (num < 0)

f g f g

printf( "Valor ilegalnn" ); break; /* Abandonar el bucle.

*/

if (num > 100) printf( "Valor demasiado grandenn" ); continue; /* No ejecutar el resto de sentencias e ir al final del bucle. */

/* Procesar el valor ledo */ . . . g while (num != 0 ); . . .

5.5 Ejercicios
Se recomienda realizar los siguientes ejercicios utilizando las diferentes construcciones iterativas presentadas. 1. Escribir un programa que calcule la suma de los 20 primeros n meros m ltiplos de 5 o de 7. u u 2. Escribir un programa que calcule la potencia de un n mero entero, dado su valor y el del expou nente. 3. Escribir un programa que lea N n meros enteros y muestre el mayor y el menor de todos ellos. u 4. Escribir un programa que escriba la tabla de multiplicar de un n mero ledo por teclado. u 5. Escribir un programa que muestre la serie de Fibonacci hasta un lmite dado. Recordar que la serie de Fibonacci se dene como

= 1

= 1

Fi Fi;
=

Fi;

6. Escribir un programa que convierta un n mero entero positivo a cualquier base de numeraci n u o dada, igual o inferior a 10. 7. Escribir un programa que determine si un n mero entero dado es primo o no. u 8. Escribir un programa que calcule el factorial de un n mero entero ledo por teclado. u 9. Escribir un programa que calcule la suma de todos los n meros m ltiplos de 5 comprendidos u u entre dos enteros ledos por teclado. u 10. Escribir un programa que muestre los 15 primeros n meros de la serie de Fibonacci.

Los autores, 2000; Edicions UPC, 2000.

5.5. Ejercicios

40

Los autores, 2000; Edicions UPC, 2000.

41

6. Tipos de datos elementales

Captulo 6

Tipos de datos elementales


Hasta el momento se ha usado implcitamente el tipo de datos entero en todos los ejemplos presentados. En este captulo entraremos en los detalles de este tipo de datos, as como de otros tipos de datos predenidos en C. C proporciona los siguientes tipos de datos elementales: n meros enteros, u caracteres, y n meros de coma otante (reales). u

6.1 Numeros enteros


En el ap ndice C se describe el formato usado para almacenar n meros enteros en memoria. Es impore u tante que el lector se familiarice con este formato para obtener una mejor compresi n de esta secci n. o o La palabra clave utilizada para especicar el tipo de datos entero es int. Dependiendo del ordenador, del sistema operativo y el compilador utilizados, el tama o de un entero en memoria vara. Sin n embargo, hoy en da la mayora de los computadores almacenan las variables del tipo int en 32 bits (4 bytes), por lo que el rango representable de n meros enteros va desde ;2147483648 hasta 2147483647 u (esto es, desde ;231 hasta 231 ; 1). Por otro lado, en el entorno IBM-PC a n existen compiladores en u donde un entero s lo ocupa 16 bits (2 bytes), con un rango entre ;32768 y 32767 (desde ;215 hasta o 15 2 ; 1). Por ejemplo, Turbo C 2.0, Visual C++ 1.5, etc. Aunque ya se ha visto previamente, el formato para declarar variables enteras es: int lista de variables;

Donde se especica una lista de nombres de variables separados por comas. Los n meros enteros en C se expresan mediante una serie de dgitos, precedidos o no por un signo u o ;. Si el n mero es positivo, el signo + es opcional. Habitualmente se utiliza la notaci n decimal, u o aunque tambi n es posible usar notaci n octal y hexadecimal. En los n meros expresados en octal, se e o u utiliza un 0 como primer dgito, mientras que en los n meros expresados en hexadecimal, el n mero u u es precedido por un 0 y una equis may scula o min scula (0x o 0X). La tabla 6.1 muestra algunos u u ejemplos de representaci n de constantes enteras en los distintos formatos. o
+

Los autores, 2000; Edicions UPC, 2000.

6.1. N meros enteros u

42

Tabla 6.1: Representaci n de enteros en decimal, octal y hexadecimal o Decimal 27 4026 -149 Octal 033 07672 -0225 Hexadecimal 0X1B 0xFBA -0x95

6.1.1 Modicadores
C dene una serie de modicadores para el tipo de datos entero. El modicador short Este modicador se emplea para representar n meros enteros de 16 bits, independientemente del prou o cesador, por lo que su rango es ;32768 32767]. As pues, hay entornos de programaci n donde el tama o y rango de una variable short int coincide con el de int. Mientras que en otros entorn nos, una variable de tipo short int ocupa la mitad de espacio y tiene un rango mucho menor. La declaraci n de variables de este tipo tiene la forma: o short int O simplemente: short lista de variables; lista de variables;

No existe ninguna manera de especicar explcitamente constantes de tipo short int. El modicador long Este modicador se emplea para representar n meros enteros con un rango mayor al permitido por u int, por lo que tambi n ocupan m s espacio en memoria. Por lo tanto las variables del tipo long e a int pueden ocupar 32 o 64 bits seg n el entorno. Habitualmente son 64 bits, lo que da un rango de u representacion de ;9223372036854775808 9223372036854775807], esto es ;263 ;263 ; 1]. La declaraci n de variables es como sigue: o long int O simplemente: long lista de variables; lista de variables;

Para especicar explcitamente que una constante es de tipo long int, debe escribirse una letra ele (may scula o min scula), justo detr s del valor constante. Cabe decir, que esto s lo es necesario u u a o en caso que el valor de la constante que se desea especicar est dentro del rango de int. Es ree o comendable el uso de L, pues l puede confundirse con el dgito 1. A continuaci n se muestra un ejemplo:

Los autores, 2000; Edicions UPC, 2000.

43

6. Tipos de datos elementales

long x; . . . x = -554L; x = 187387632; El modicador signed Es el modicador usado por defecto para todo dato de tipo int, por lo que no suele utilizarse de forma explcita. En cualquier caso, las declaraciones tiene la forma: signed int O simplemente: int lista de variables; lista de variables;

El modicador unsigned Este modicador permite especicar n meros enteros sin signo. Como consecuencia de eliminar el u signo de la representaci n, el rango de valores positivos se amplia hasta 0 65535] si se emplean 16 o o bits, o 0 4294967295] si se emplean 32 bits. La declaraci n de variables se realiza como: unsigned int O simplemente: unsigned lista de variables; lista de variables;

Pueden especicarse constantes de tipo unsigned, escribiendo una letra u may scula justo detr s u a del valor constante, como en: unsigned x; . . . x = 45728; x = 345U; Finalmente, cabe comentar que el modicador unsigned puede combinarse con short y long. Por ejemplo, los datos de tipo unsigned long tienen un rango v lido de 0 264 ; 1], es a decir 0 18446744073709551615].

6.1.2 Resumen
La tabla 6.2 resume los distintos tipos de datos enteros que se han presentado, mostrando su rango, ocupaci n de memoria en bits y el modicador de formato para printf y scanf. o

Los autores, 2000; Edicions UPC, 2000.

6.2. Caracteres

44

Tabla 6.2: Resumen de tipos de datos enteros Tipo int unsigned int short int unsigned short int long int unsigned long int

Rango

2147483648 2147483647]

0 4294967295] 32768 32767] 0 65535]


63

2
64

63

0 2

;
1]

1]

Tama no 32 bits 32 bits 16 bits 16 bits 64 bits 64 bits

Formato %d %u %d %u %ld %lu

6.2 Caracteres
Este tipo de datos se identica con la palabra reservada char. Comprende un conjunto ordenado y nito de caracteres, representados mediante c digos num ricos. La codicaci n m s extendida es el o e o a est ndar ASCII que utiliza 8 bits. As pues, el tipo de datos char es internamente tratado como a un entero, aunque puede manipularse como un car cter propiamente. El ap ndice D muestra la tabla a e completa de c digos ASCII y los caracteres asociados. La declaraci n de variables se realiza como: o o char lista de variables;

La forma habitual de expresar una constante de tipo char es mediante el car cter correspondiente a entre comillas: char x, y; . . . x = f; y = ?; x = 5; Debido a la representaci n interna de los caracteres mediante n meros enteros, pueden realizarse o u operaciones aritm ticas como: F+5 , o lo que es lo mismo, 70 + 53 = 123, que equivale al e a car cter f; o como F+5, esto es 70 + 5 = 75 que equivale al car cter K. a Por la misma raz n tambi n se establece un orden entre los caracteres, lo que permite usar los o e operadores relacionales habituales. As pues, la comparaci n a <= h da como resultado el o valor cierto, o la comparaci n K > V da como resultado el valor falso. o

6.2.1 Caracteres especiales


Algunos de los caracteres denidos por el est ndar ASCII no son directamente imprimibles, por lo que a para utilizarlos en un programa es necesario un mecanismo especial. En realidad, C ofrece dos: Puede usarse el c digo ASCII asociado al car cter, que puede representarse tanto en decimal, o a o octal o hexadecimal. Por ejemplo, el car cter n9 representa un tabulador mediante su c digo a o ASCII en decimal; el car cter n040 corresponde al espacio en blanco mediante su c digo a ASCII en octal, etc. O bien, pueden usarse secuencias de escape especiales, como nn , para forzar un salto de lnea, que hemos utilizado en ejemplos anteriores. La siguiente tabla muestra los m s utilizados: a

Los autores, 2000; Edicions UPC, 2000.

45

6. Tipos de datos elementales

Tabla 6.3: Caracteres interpretados como enteros Tipo char signed char unsigned char

; ;

Rango
128 127] 128 127] 0 255]

Tama no 1 byte 1 byte 1 byte

Formato %c %d %u

na nn nf nr nt n n" n0
6.2.2 Enteros y el tipo char

C digo o

Signicado pitido salto de lnea salto de p gina a principio de la misma lnea tabulador ap strofe o comillas car cter nulo a

Puesto que el tipo char est internamente representado mediante c digos num ricos de 8 bits, una a o e variable de este tipo puede interpretarse tambi n como una variable entera. As pues, pueden utilizarse e tambi n los modicadores signed y unsigned. Los rangos de valores se muestran en la tabla 6.3. e

6.2.3 Conversiones de tipos


En general, en C puede convertirse el tipo de una variable o constante a otro tipo cualquiera. Para ello, basta con escribir delante de dicha variable el nuevo tipo entre par ntesis. Aunque no profundizaremos e en la conversi n de tipos en C, veremos su utilidad en este caso, para obtener el c digo ASCII de un o o car cter y viceversa. a En el ejemplo de la izquierda, se asigna a la variable cod el c digo ASCII del car cter A alo a a macenado en la variable c, que es 65. En el ejemplo de la derecha, se asigna a la variable c el car cter correspondiente al c digo ASCII 98 almacenado en la variable cod, que es el caracter b. o int cod; char c; . . . c = A; cod = (int) c; . . . Para concluir, un par de aspectos pr cticos: a Si la variable c almacena alg n car cter del rango 0...9, puede obtenerse su valor u a num rico equivalente (no confundir con el c digo ASCII) mediante la sentencia: e o i = (int)c - (int)0; int cod; char c; . . . cod = 98; c = (char) cod; . . .

Los autores, 2000; Edicions UPC, 2000.

6.3. N meros reales u

46

Tabla 6.4: Resumen de tipos de datos reales Tipo float double long double

1 1754945

2 225073858507202

1 797693134862312 8 4

: :

3 4028232

: :

Rango

5 9

: :::E ; : :::E

E; E E; E
38 + 38 4932

Tama no 4 bytes
308

Formato %f, %e, %g %f, %e, %g %f, %e, %g

8 bytes 16 bytes

+ 308

+ 4932

Si i es una variable entera con un valor en el rango pondiente de la siguiente forma: c = (char)((int)0 + i);

0 9]

, puede obtenerse el car cter corresa

6.3 Numeros reales


Nuevamente, en el ap ndice C se describe el formato de coma otante usado para almacenar n meros e u reales. Es importante que el lector se familiarice con este formato para obtener una mejor comprensi n o de esta secci n. o En C existen b sicamente dos tipos de n meros reales, los de precisi n simple (float) y los de a u o precisi n doble (double). Un valor de tipo float ocupa 4 bytes (32 bits) y permite representar o n meros reales con 8 dgitos de precisi n. Un valor de tipo double ocupa 8 bytes (64 bits) y permite u o representar n meros reales con 16 dgitos de precisi n (ver Tab. 6.4). u o Las constantes reales pueden representarse mediante dos notaciones: la decimal, constituida por una serie de dgitos donde la parte decimal se sit a detr s de un punto, por ejemplo -5.034 o 443.43; u a y la notaci n cientca o exponencial, formada por una parte en notaci n decimal que representa la o o mantisa, seguida del car cter E o e y un n mero entero que representa el exponente, como por a u ejemplo: -3.56E67 o 7.9e-15. Por defecto, las constantes reales se consideran de tipo double. Para especicar explcitamente constantes de tipo float, debe escribirse una letra efe may scula tras la constante, como en el siu guiente ejemplo: double x; float y; . . . x = 34E23; y = 2.3E12F; Algunos compiladores admiten el uso del modicador long para el tipo double. Este tipo de variables se almacenan en 16 bytes y proporcionan precisi n cu druple (32 dgitos). o a Para concluir, veamos un ejemplo de uso de n meros reales en un programa que calcula el sumau torio

1 X
i
=1

con un error inferior a un valor

, mientras que

1000

. Expresando el error

Los autores, 2000; Edicions UPC, 2000.

47

6. Tipos de datos elementales

matem ticamente tenemos: a

k X i
=1

i ;

k; X i
=1

i j<

A continuaci n se muestra un programa que realiza dicho c lculo: o a #include <stdio.h> #define LIMITE 1000 void main ()

int i, fin; float suma, t, epsilon; suma = 0.0F; fin = 0; i = 1; scanf( "%f", &epsilon ); while (!fin)

g g

t = 1.0F / (float)i; suma = suma + t; i++; fin = ((t < epsilon) || (i > LIMITE));

printf( "La suma es %fnn", suma );

Obs rvese el uso del modicador de formato %f para la entrada y salida de valores de coma otante, e y el uso de la variable fin para controlar la terminaci n del bucle. o En el chero de cabeceras math.h (#include <math.h>), perteneciente a la librera est ndar a (ver Ap. B), se denen una serie de funciones matem ticas para operar con valores reales, como: a sqrt para la raz cuadrada, sin y cos para senos y cosenos, etc.

6.4 Ejercicios
1. Escribir un programa que cuente el n mero de veces que aparece una letra en una secuencia de u caracteres acabada en . . 2. Escribir un programa que lea un car cter de teclado e informe de si es alfab tico, num rico, a e e blanco o un signo de puntuaci n. o 3. Escribir un programa que convierta una secuencia de dgitos entrados por teclado al n mero ente u ro correspondiente. Sup ngase que el primer dgito ledo es el de mayor peso. Obs rvese tambi n o e e que el peso efectivo de cada dgito ledo es desconocido hasta haber concluido la introducci n de o la secuencia.

Los autores, 2000; Edicions UPC, 2000.

6.4. Ejercicios

48

4. Sean las variables enteras i y j con valores 5 y 7, respectivamente. Y las variables de a a coma otante f y g con valores 5:5 y ;3:25, respectivamente. Cu l ser el resultado de las siguientes asignaciones? (a) i = i % 5; (b) f = (f - g) / 2; (c) j = j * (i - 3); (d) f = f % g; (e) i = i / (j - 2); 5. Escribir un programa que calcule y muestre por pantalla las races de una ecuaci n de segundo o 2 grado, leyendo previamente los coecientes A, B y C de la misma: Ax + Bx + C = 0. 6. Escribir un programa que calcule el permetro de una circunferencia y que lo muestre por pantalla con cuatro decimales de precisi n. Si el radio introducido por el usuario es negativo, el permetro o resultante ser 0. a 7. Escribir un programa para calcular de forma aproximada el n mero e. Recordar que u

1 X
i
=0

1 !

Los autores, 2000; Edicions UPC, 2000.

49

7. Tipos de datos estructurados: Tablas

Captulo 7

Tipos de datos estructurados: Tablas


En este captulo veremos algunos de los mecanismos que C ofrece para la creaci n de tipos de datos o complejos. Estos se construyen, en un principio, a partir de los tipos de datos elementales predenidos por el lenguaje (ver Cap. 6). Comenzaremos hablando del tipo abstracto de datos tabla, tanto de una (vectores), dos (matrices) o m ltiples dimensiones. En C existe un tratamiento especial para los vectores de caracteres, por lo que u dedicaremos una parte de este captulo a su estudio. Se deja para el captulo 8 el estudio de otros tipos de datos estructurados, como las estructuras, las uniones, y los tipos de datos enumerados. Las tablas en C son similares a las que podemos encontrar en otros lenguajes de programaci n. o Sin embargo, se denen de forma diferente y poseen algunas peculiaridades derivadas de la estrecha relaci n con los punteros. Volveremos a esto m s adelante en el captulo 9. o a

7.1 Vectores
Los vectores, tambi n llamados tablas unidimensionales, son estructuras de datos caracterizadas por: e Una colecci n de datos del mismo tipo. o Referenciados mediante un mismo nombre. Almacenados en posiciones de memoria fsicamente contiguas, de forma que, la direcci n de o memoria m s baja corresponde a la del primer elemento, y la direcci n de memoria m s alta a o a corresponde a la del ultimo elemento. El formato general para la declaraci n de una variable de tipo vector es el siguiente: o tipo de datos nombre tabla [tamao]; n donde: tipo de datos indica el tipo de los datos almacenados por el vector. Recordemos que todos los elementos del vector son forzosamente del mismo tipo. Debe aparecer necesariamente en la declaraci n, puesto que de ella depende el espacio de memoria que se reservar para almacenar o a el vector.

Los autores, 2000; Edicions UPC, 2000.

7.1. Vectores

50

N-1

...
Primer elemento del vector ltimo elemento del vector

Figura 7.1: Representaci n gr ca de un vector de N elementos o a nombre tabla es un identicador que usaremos para referiremos tanto al vector como un todo, como a cada uno de sus elementos. tamao es una expresi n entera constante que indica el n mero de elementos que contendr el n o u a vector. El espacio ocupado por un vector en memoria se obtiene como el producto del n mero de u elementos que lo componen y el tama o de cada uno de estos. n

7.1.1 Consulta
El acceso a un elemento de un vector se realiza mediante el nombre de este y un ndice entre corche tes ([ ]). El ndice representa la posici n relativa que ocupa dicho elemento dentro del vector y se o especica mediante una expresi n entera (normalmente una constante o una variable). Formalmente: o nombre vector[ndice]; A continuaci n se muestran algunas formas v lidas de acceso a los elementos de un vector: o a int contador[10]; int i, j, x; . . . x = contador[1]; x = contador[i]; x = contador[i * 2 + j]; . . . Como muestra la gura 7.1, el primer elemento de un vector en C se sit a en la posici n 0, mientras u o u que el ultimo lo hace en la posici n N ; 1 (N indica el n mero de elementos del vector). Por esta o raz n, el ndice para acceder a los elementos del vector debe permanecer entre estos dos valores. Es o responsabilidad del programador garantizar este hecho, para no acceder a posiciones de memoria fuera del vector, lo cual producira errores imprevisibles en el programa.

7.1.2 Asignaci n o
La asignaci n de valores a los elementos de un vector se realiza de forma similar a como se consultan. o Ve moslo en un ejemplo: a

Los autores, 2000; Edicions UPC, 2000.

51

7. Tipos de datos estructurados: Tablas

int contador[3]; . . . contador[0] = 24; contador[1] = 12; contador[2] = 6; En muchas ocasiones, antes de usar un vector (una tabla en general) por primera vez, es necesario dar a sus elementos un valor inicial. La manera m s habitual de inicializar un vector en tiempo de a ejecuci n consiste en recorrer secuencialmente todos sus elementos y darles el valor inicial que les o corresponda. Ve moslo en el siguiente ejemplo, donde todos los elementos de un vector de n meros a u enteros toman el valor 0: #define TAM 100 void main()

int vector[TAM], i; for (i= 0; i< TAM; i++) vector[i] = 0;

N tese que el bucle recorre los elementos del vector empezando por el elemento 0 (i=0) y hasta el o elemento TAM-1 (condici n i<TAM). o Existe tambi n un mecanismo que permite asignar un valor a todos los elementos de un tabla con e una sola sentencia. Concretamente en la propia declaraci n de la tabla. La forma general de inicializar o una tabla de cualquier n mero de dimensiones es la siguiente: u tipo de datos nombre tabla [tam1]...[tamN] =

lista valores

g;

La lista de valores no deber contener nunca m s valores de los que puedan almacenarse en la tabla. a a Veamos algunos ejemplos: int contador[3] = f24, 12, 6g; /* Correcto */ char vocales[5] = fa, e, i, o, ug; /* Correcto */ int v[4] = f2, 6, 8, 9, 10, 38g; /* Incorrecto */ Finalmente, cabe destacar que no est permitido en ning n caso comparar dos vectores (en general a u dos tablas de cualquier n mero de dimensiones) utilizando los operadores relacionales que vimos en la u secci n 3.4.3. Tampoco est permitida la copia de toda una tabla en otra con una simple asignaci n. Si o a o se desea comparar o copiar toda la informaci n almacenada en dos tablas, deber hacerse elemento a o a elemento. Los mecanismos de acceso descritos en esta secci n son id nticos para las matrices y las tablas mulo e tidimensionales. La unica diferencia es que ser necesario especicar tantos ndices como dimensiones a posea la tabla a la que se desea acceder. Esto lo veremos en las siguientes secciones.

Los autores, 2000; Edicions UPC, 2000.

7.1. Vectores

52

7.1.3 Ejemplos
A continuaci n se muestra un programa que cuenta el n mero de apariciones de los n meros 0, 1, 2 y o u u 3 en una secuencia de enteros acabada en ;1.

#include <stdio.h> void main () int num, c0=0, c1=0, c2=0, c3=0; scanf("%d", &num); while (num != -1)

g g

if (num == 0) c0++; if (num == 1) c1++; if (num == 2) c2++; if (num == 3) c3++; scanf( "%d", &num );

printf( "Contadores:%d, %d, %d, %dnn", c0, c1, c2, c3 );

Qu ocurrira si tuvi semos que contar las apariciones de los cien primeros n meros enteros? e e u Deberamos declarar cien contadores y escribir cien construcciones if para cada caso? La respuesta, como era de esperar, se halla en el uso de vectores. Ve moslo en el siguiente programa: a #include <stdio.h> #define MAX 100 void main () int i, num, cont[MAX]; for (i= 0; i< MAX; i++) cont[i] = 0; scanf("%d", &num); while (num != -1) f if ((num >= 0) && (num <= MAX)) cont[num]++; scanf( "%d", &num );

g g

for (i= 0; i< MAX; i++) printf( "Contador [%d] = %dnn", i, cont[i] );

Veamos nalmente otro ejemplo en el que se muestra c mo normalizar un vector de n meros reales. o u La normalizaci n consiste en dividir todos los elementos del vector por la raz cuadrada de la suma de o sus cuadrados. Destaca el uso de la constante MAX para denir el n mero de elementos del vector y u de la funci n sqrt para el c lculo de races cuadradas. o a

Los autores, 2000; Edicions UPC, 2000.

53

7. Tipos de datos estructurados: Tablas

#include <math.h> #include <stdio.h> #define MAX 10 void main()

float vector[MAX], modulo; int i; /* Lectura del vector. */ for (i= 0; i< MAX; i++) scanf("%f", &vector[i]); /* Calcular mdulo. */ o modulo = 0.0; for (i= 0; i< MAX; i++) modulo = modulo + (vector[i] * vector[i]); modulo = sqrt(modulo); /* Normalizar */ for (i= 0; i< MAX; i++) vector[i] /= modulo; /* Escritura del vector. */ for (i= 0; i< MAX; i++ ) printf( "%f ", vector[i] );

7.2 Matrices
Las matrices, tambi n llamadas tablas bidimensionales, no son otra cosa que vectores con dos dimene siones. Por lo que los conceptos de acceso, inicializaci n, etc. son similares. o La declaraci n de una variable matriz tiene la forma siguiente: o tipo de datos nombre tabla [tamao dim1][tamao dim2]; n n Donde tamao dim1 y tamao dim2 indican, respectivamente, el n mero de las y de n n u columnas que componen la matriz. La gura 7.2 muestra la representaci n gr ca habitual de una o a matriz de datos. Otro hecho importante es que las matrices en C se almacenan por las. Es decir, que los elementos de cada la se sit an en memoria de forma contigua. As pues, en la matriz de la gura anterior, el u primer elemento almacenado en memoria es el (0,0), el segundo el (0,1), el tercero el (0,2), . . . , (0,M-1), despu s (1,0), y as sucesivamente hasta el ultimo elemento, es decir (N-1,M-1). e

Los autores, 2000; Edicions UPC, 2000.

7.2. Matrices

54

0 0 1 Primer elemento de la matriz 2 3

M-1

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


ltimo elemento de la matriz

N-1

Figura 7.2: Representaci n gr ca de una matriz de N las y M columnas o a

7.2.1 Consulta
El acceso a un elemento de una matriz se realiza mediante el nombre de esta y dos ndices (uno para cada dimensi n) entre corchetes. El primer ndice representa la la y el segundo la columna en que se o encuentra dicho elemento. Tal como muestra la gura 7.2, el ndice de las las tomar valores entre 0 y a a u el n mero de las menos 1, mientras que el ndice de las columnas tomar valores entre 0 y el n mero u de columnas menos 1. Es responsabilidad del programador garantizar este hecho. nombre matriz[ndice 1][ndice 2];

7.2.2 Asignaci n o
Comentaremos unicamente la inicializaci n de una matriz en la propia declaraci n. El siguiente ejemo o plo declara una matriz de tres las y cuatro columnas y la inicializa. Por claridad, se ha usado identaci n o en los datos, aunque hubiesen podido escribirse todos en una sola lnea. int mat[3][4] =

24, 12, 6, 17, 15, 28, 78, 32, 0, 44, 3200 , -34 g;

La inicializaci n de matrices, y en general de tablas multidimensionales, puede expresarse de forma o m s clara agrupando los valores mediante llaves (f g), siguiendo la estructura de la matriz. As pues, a el ejemplo anterior tambi n podra escribirse como: e int mat[3][4] =

ff f f g;

24, 12, 6, 17 g, 15, 28, 78, 32 g, 0, 44, 3200 , -34

Los autores, 2000; Edicions UPC, 2000.

55

7. Tipos de datos estructurados: Tablas

7.2.3 Ejemplo
El siguiente ejemplo calcula la matriz traspuesta de una matriz de enteros. La matriz tendr unas a dimensiones m ximas seg n la constante MAX. a u #include <stdio.h> #define MAX 20 void main()

int filas, columnas, i, j; int matriz[MAX][MAX], matrizT[MAX][MAX]; /* Lectura matriz */ printf( "Num. filas, Num. columnas: " ); scanf( "%d%d", &filas, &columnas ); printf ("Introducir matriz por filas:" ); for (i= 0; i< filas; i++) for (j= 0; j< columnas; j++)

f g

printf( "nnmatriz[%d][%d] = ", i, j ); scanf( "%d", &matriz[i][j] );

/* Traspuesta */ for (i= 0; i< filas; i++) for (j= 0; j< columnas; j++) matrizT[j][i] = matriz[i][j]; /* Escritura del resultado */ for (i= 0; i< filas; i++) for (j= 0; j< columnas; j++) printf( "nnmatrizT[%d][%d] = %d ", i, j, matrizT[i][j] );

Obs rvese que para recorrer todos los elementos de una matriz es necesario el empleo de dos bucles e u anidados que controlen los ndices de las y columnas (siempre entre 0 y el n mero de las o columnas menos 1). En este ejemplo todos los recorridos se realizan por las, es decir, que primero se visitan todos los elementos de una la, luego los de la siguiente, etc. Finalmente, cabe comentar que para el recorrido de tablas multidimensionales ser necesario el empleo de tantos bucles como dimensiones a tenga la tabla.

7.3 Tablas multidimensionales


Este tipo de tablas se caracteriza por tener tres o m s dimensiones. Al igual que vectores y matrices, a todos los elementos almacenados en ellas son del mismo tipo de datos. La declaraci n de una tabla multidimensional tiene la forma siguiente: o

Los autores, 2000; Edicions UPC, 2000.

7.3. Tablas multidimensionales

56

M-1

... ... ...


Primer elemento de la tabla

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

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


2

0 1 2 3

...
ltimo elemento de la tabla 0

N-1

Figura 7.3: Representaci n gr ca de una tabla de tres dimensiones: N o a

tipo de datos nombre tabla [tamao dim1] ...[tamao dimN]; n n Para acceder a un elemento en particular ser necesario usar tantos ndices como dimensiones: a nombre vector[ndice 1] ...[ndice N]; Aunque pueden denirse tablas de m s de tres dimensiones, no es muy habitual hacerlo. La gua ra 7.3 muestra como ejemplo la representaci n gr ca habitual de una tabla de tres dimensiones. o a

7.3.1 Ejemplo
El siguiente ejemplo muestra el empleo de una tabla multidimensional. Concretamente, el programa utiliza una tabla de 3 dimensiones para almacenar 1000 n meros aleatorios. Para generar n meros u u aleatorios se usa la funci n rand de la librera est ndar stdlib.h. Tambi n se ha usado la funci n o a e o getchar (stdio.h), que interrumpe el programa y espera a que el usuario presione una tecla. #include <stdio.h> #include <stdlib.h> #define DIM 10 void main()

int tabla random [DIM][DIM][DIM], a, b, c; for (a= 0; a< DIM; a++)

Los autores, 2000; Edicions UPC, 2000.

57

7. Tipos de datos estructurados: Tablas

for (b= 0; b< DIM; b++) for (c= 0; c< DIM; c++) tabla random[a][b][c] = rand(); /* Muestra series de DIM en DIM elementos. for (a= 0; a< DIM; a++) for (b= 0; b < DIM; b++) */

for (c= 0; c < DIM; c++)

f g g g

printf( "nn tabla[%d][%d][%d] = ", a, b, c ); printf( "%d", tabla random[a][b][c] );

printf( "nnPulse ENTER para seguir" ); getchar();

7.4 Cadenas de caracteres


Las cadenas de caracteres son vectores de tipo car cter (char) que reciben un tratamiento especial para a simular el tipo de datos string, presente en otros lenguajes de programaci n. o Para que un vector de caracteres pueda ser considerado como una cadena de caracteres, el ultimo u de los elementos utiles del vector debe ser el car cter nulo (c digo ASCII 0). Seg n esto, si se quiere a o a declarar una cadena formada por N caracteres, deber declararse un vector con N + 1 elementos de tipo car cter. Por ejemplo, la declaraci n char cadena[6]; reserva suciente espacio en memoria a o para almacenar una cadena de 5 caracteres, como la palabra "casco":

c a s

c o \0

En C pueden denirse constantes correspondientes a cadenas de caracteres. Se usan comillas dobles para delimitar el principio y el nal de la cadena, a diferencia de las comillas simples empleadas con las constantes de tipo car cter. Por ejemplo, la cadena constante "H" tiene muy poco que ver con el a car cter constante H, si observamos la representaci n interna de ambos: a o

"H"

H \0

7.4.1 Asignaci n o
Mientras que la consulta de elementos de una cadena de caracteres se realiza de la misma forma que con los vectores, las asignaciones tienen ciertas peculiaridades.

Los autores, 2000; Edicions UPC, 2000.

7.4. Cadenas de caracteres

58

Como en toda tabla, puede asignarse cada car cter de la cadena individualmente. No deber olvia a darse en ning n caso que el ultimo car cter v lido de la misma debe ser el car cter nulo (n0). El u a a a siguiente ejemplo inicializa la cadena de caracteres cadena con la palabra "casco". N tese que o las tres ultimas posiciones del vector no se han usado. Es m s, aunque se les hubiese asignado alg n a u car cter, su contenido sera ignorado. Esto es, el contenido del vector en las posiciones posteriores al a car cter nulo es ignorado. a char cadena[10]; . . . cadena[0] cadena[1] cadena[2] cadena[3] cadena[4] cadena[5]

= = = = = =

c; a; s; c; o; n0;

La inicializaci n de una cadena de caracteres durante la declaraci n puede hacerse del mismo modo o o que en los vectores, aunque no debe olvidarse incluir el car cter nulo al nal de la cadena. Sin embargo, a existe un m todo de inicializaci n propio de las cadena de caracteres, cuyo formato general es: e o char nombre [tamao] = "cadena"; n Usando este tipo de inicializaci n, el car cter nulo es a adido autom ticamente al nal de la cadena. o a n a As pues, una inicializaci n tpica de vectores como la siguiente: o char nombre[10] =

N, U, R, I, A, n0

g;

puede hacerse tambi n de forma equivalente como: e char nombre[10] = "NURIA"; Finalmente, la inicializaci n anterior puede hacerse sin necesidad de especicar el tama o del veco n tor correspondiente. En este caso, el compilador se encarga de calcularlo autom ticamente, reservando a espacio de memoria suciente para almacenar la cadena, incluyendo el car cter nulo al nal. As pues, a la siguiente declaraci n reserva memoria para almacenar 6 caracteres y los inicializa adecuadamente o con las letras de la palabra NURIA: char nombre[] = "NURIA"; La cadena vaca Otra curiosidad de las cadenas de caracteres se reere a la cadena vaca, " " , que consta unicamente del car cter nulo. Puesto que los caracteres posteriores al car cter nulo son ignorados, convertir una a a cadena con cualquier valor almacenado a la cadena vaca es tan simple como asignar el car cter nulo a a la posici n 0 de dicha cadena. He aqu un ejemplo: o char cadena [12] = "Una frase"; . . . cadena[0] = n0; /* Ahora es una cadena vaca */

Los autores, 2000; Edicions UPC, 2000.

59

7. Tipos de datos estructurados: Tablas

10

11

"Una frase" U n a

\0

cadena[0] = \0;
0 1 2 3 4 5 6 7 8 9 10 11

"" \0 n a

\0

7.4.2 Manejo de cadenas de caracteres


Aunque C no incorpora en su denici n operadores para el manejo de cadenas de caracteres, todo como pilador de C proporciona una librera est ndar (string.h) con funciones para facilitar su utilizaci n. a o Destacar algunas de ellas: strlen para obtener la longitud de la cadena, sin contar el car cter nulo, a strcpy para copiar una cadena en otra, strcat para concatenar dos cadenas, strcmp para comparar dos cadenas, etc. Para m s informaci n sobre estas y otras funciones, consultar el ap ndice B. a o e Entrada y salida En cuanto a la entrada y salida de cadenas de caracteres, existe un formato especial %s que puede utilizarse en las funciones scanf y printf. Por ejemplo, la siguiente sentencia leer una cadena de a caracteres en la variable cad. S lo se asignar n caracteres mientras no sean caracteres blancos, tabuo a ladores o saltos de lnea. Por lo tanto, el empleo de %s s lo tendr sentido para la lectura de palabras. o a Adem s del formato %s existen los formatos %[abc] y %[abc], que permiten leer respectivamente a una cadena de caracteres hasta encontrar alg n car cter del conjunto fa, b, cg, o bien hasta no encontrar u a a un caracter del conjunto fa, b, cg. En cualquier caso el car cter del conjunto fa, b, cg no es ledo. Ver el ap ndice B para m s informaci n sobre el empleo de scanf y la lectura de cadenas de caracteres. e a o char cad[20]; . . . scanf("%s", cad); N tese que, en el ejemplo, no se ha antepuesto el smbolo & a la variable cad. Por el momento, o teng moslo en mente y esperemos hasta el captulo 9 para comprender a qu se debe este hecho. a e La librera est ndar de entrada y salida (stdio.h) proporciona adem s las funciones gets y a a puts, que permiten leer de teclado y mostrar por pantalla una cadena de caracteres completa, respectivamente (ver el ap ndice B para m s detalles). e a

Los autores, 2000; Edicions UPC, 2000.

7.4. Cadenas de caracteres

60

7.4.3 Ejemplos
Para nalizar, veamos un par de ejemplos de manejo de cadenas de caracteres. El siguiente programa cuenta el n mero de veces que se repite una palabra en una frase. El programa u emplea la funci n de comparaci n de cadenas strcmp. Dicha funci n devuelve 0 en caso de que las o o o cadenas comparadas sean iguales (ver Sec. B.1). #include <stdio.h> #include <string.h> #define MAXLIN 100 void main() char pal[MAXLIN]; char palfrase[MAXLIN]; char c; int total = 0; printf( "nnPALABRA:" ); scanf( "%s", pal ); printf( "nnFRASE:" ); c = ; while (c != nn) /* La que buscamos. */ /* Una palabra de la frase.

*/

f g g

scanf( "%s%c", palfrase, &c ); if (strcmp(pal, palfrase) == 0) total++;

printf( "nnLa palabra %s aparece %d veces.", pal, total );

A continuaci n se muestra otro ejemplo que hace uso de las cadenas de caracteres. El programa lee o dos cadenas de caracteres, las concatena, convierte las letras min sculas en may sculas y viceversa, y u u nalmente escribe la cadena resultante. #include <stdio.h> #include <string.h> void main() char cad1[80], cad2[80], cad3[160]; int i, delta; printf( "Introduzca la primera cadena:nn" ); gets(cad1); printf( "Introduzca la segunda cadena:nn" ); gets( cad2 ); /* cad3 = cad1 + cad2 */ strcpy( cad3, cad1 );

Los autores, 2000; Edicions UPC, 2000.

61

7. Tipos de datos estructurados: Tablas

strcat( cad3, cad2 ); i = 0; delta = a - A; while (cad3[i] != n0)

g g

if ((cad3[i] >= a) cad3[i] -= delta; else if ((cad3[i] >= cad3[i] += delta; i++;

&& (cad3[i] <= z)) /* Convierte a mayscula */ u A) && (cad3[i] <= Z)) /* Convierte a minscula */ u

printf( "La cadena resultante es:

%s

nn",

cad3 );

7.5 Ejercicios
1. D nde est el error en el siguiente programa? o a void main()

int vector [10]; int x = 1; for (x= 1; x<= 10; x++) vector[x] = 23;

2. Escribir un programa que lea del teclado un vector de 10 n meros enteros, lo invierta y nalmente u lo muestre de nuevo. 3. Escribir un programa que cuente el n mero de palabras de m s de cuatro caracteres en una frase. u a se almacena en forma de vector cuyo ultimo elemento es el car cter .. Esta a 4. Escribir un programa que lea del teclado dos n meros enteros de hasta 20 dgitos y los sume. u Usar vectores para almacenar los dgitos de cada n mero. u 5. Escribir un programa que decida si una palabra es palndroma o no. La palabra se almacena en un vector de caracteres acabado en .. 6. Escribir un programa para calcular la moda de un conjunto de n meros enteros. La moda es el u valor que se repite m s veces. a 7. D nde est el error en el siguiente programa? o a void main()

int matriz [10][3], x, y;

Los autores, 2000; Edicions UPC, 2000.

7.5. Ejercicios

62

for (x= 0; x< 3; x++) for (y= 0; y< 10; y++) matrix[x][y] = 0;

8. Escribir un programa que inicialice cada elemento de una matriz de enteros con el valor de la suma del n mero de la y columna en que est situado. u a 9. Escribir un programa que calcule la suma de dos matrices de enteros. 10. Escribir un programa que calcule los puntos de silla de una matriz de enteros. Un elemento de una matriz es un punto de silla si es el mnimo de su la y el m ximo de su columna. a 11. Escribir un programa que determine si una matriz es sim trica. e 12. Escribir un programa que multiplique dos matrices. 13. Escribir un programa que lea una frase del teclado y cuente los espacios en blanco. 14. Escribir un programa que, dada una cadena de caracteres y un entero correspondiente a una posici n v lida dentro de ella, genere una nueva cadena de caracteres que contenga todos los o a caracteres a la izquierda de dicha posici n, pero en orden inverso. o 15. Escribir un programa que, dada una cadena de caracteres, la limpie de caracteres blancos. Por ejemplo, la cadena "Esto es una frase" deber transformarse en "Estoesunafrase". a Escribir dos versiones, una utilizando una cadena auxiliar y otra versi n que realice los cambios o sobre la misma cadena. 16. Escribir un programa que lea dos cadenas de caracteres, las compare e informe de si son iguales o diferentes. No usar la funci n de la librera est ndar strcmp. o a

Los autores, 2000; Edicions UPC, 2000.

63

8. Otros tipos de datos

Captulo 8

Otros tipos de datos


En este captulo veremos los restantes mecanismos que C ofrece para la creaci n y manejo de tipo de o datos complejos. Concretamente las estructuras y las uniones. Por otra parte, se incluye tambi n un e apartado que estudia los tipos de datos enumerados y otro donde se trata la denici n de nuevos tipos o de datos por parte del programador.

8.1 Estructuras
En el captulo 7 hemos estudiado el tipo abstracto de datos tabla, formado por un conjunto de elementos todos ellos del mismo tipo de datos. En una estructura, sin embargo, los elementos que la componen pueden ser de distintos tipos. As pues, una estructura puede agrupar datos de tipo car cter, enteros, a cadenas de caracteres, matrices de n meros . . . , e incluso otras estructuras. En cualquier caso, es u habitual que todos los elementos agrupados en una estructura tengan alguna relaci n entre ellos. En o adelante nos referiremos a los elementos que componen una estructura como campos. La denici n de una estructura requiere especicar un nombre y el tipo de datos de todos los campos o que la componen, as como un nombre mediante el cual pueda identicarse toda la agrupaci n. Para o ello se utiliza la palabra reservada struct de la forma siguiente: struct nombre estructura

g;

tipo campo 1 tipo campo 2 . . . tipo campo N

nombre campo 1; nombre campo 2; nombre campo N;

El siguiente ejemplo dene una estructura denominada cliente en la que puede almacenarse la cha bancaria de una persona. El signicado de los diferentes campos es obvio:

struct cliente char long int nombre[100]; dni;

Los autores, 2000; Edicions UPC, 2000.

8.1. Estructuras

64

char long int float

domicilio[200]; no cuenta; saldo;

Puede decirse que la denici n de una estructura corresponde a la denici n de una plantilla o o gen rica que se utilizar posteriormente para declarar variables. Por tanto la denici n de una estructura e a o no representa la reserva de ning n espacio de memoria. u

8.1.1 Declaraci n de variables o


La declaraci n de variables del tipo denido por una estructura puede hacerse de dos maneras difereno tes. Bien en la misma denici n de la estructura, bien posteriormente. La forma gen rica para el primer o e caso es la siguiente: struct nombre estructura

tipo campo 1 nombre campo 1; nombre campo 2; tipo campo 2 . . . nombre campo N; tipo campo N lista de variables;

N tese que al declararse las variables al mismo tiempo que se dene la estructura, el nombre de esta o junto a la palabra reservada struct se hace innecesario y puede omitirse. Por otra parte, suponiendo que la estructura nombre estructura se haya denido previamente, la declaraci n de variables en el segundo caso sera: o struct nombre estructura lista de variables; Siguiendo con el ejemplo anterior, seg n la primera variante de declaraci n, podramos escribir: u o

struct char nombre[100]; long int dni; char domicilio[200]; long int no cuenta; float saldo; antiguo cliente, nuevo cliente;

O bien, de acuerdo con la segunda variante donde se asume que la estructura cliente se ha denido previamente: struct cliente antiguo cliente, nuevo cliente;

Los autores, 2000; Edicions UPC, 2000.

65

8. Otros tipos de datos

8.1.2 Acceso a los campos


Los campos de una estructura de manejan habitualmente de forma individual. El mecanismo que C proporciona para ello es el operador punto (.). La forma general para acceder a un campo en una variable de tipo estructura es el siguiente: variable.nombre campo As pues, podramos acceder a los campos de la variable nuevo cliente como el nombre, n mero de dni o el saldo de la siguiente forma: u nuevo cliente.nombre nuevo cliente.dni nuevo cliente.saldo

8.1.3 Asignaci n o
La asignaci n de valores a los campos de una variable estructura puede realizarse de dos formas difeo rentes. Por un lado, accediendo campo a campo, y por otro, asignando valores para todos los campos en el momento de la declaraci n de la variable. o A continuaci n se muestran algunas posibles maneras de asignar datos a los campos individuales o de la variable nuevo cliente declarada previamente. Como puede verse, cada campo es tratado como si de una variable se tratase, con lo que pueden usarse funciones como strcpy para copiar una cadena de caracteres sobre un campo de ese tipo, o gets para leerla del teclado, etc. strcpy( nuevo cliente.nombre, "Federico Sancho Buch" ); nuevo cliente.dni = 23347098; gets( nuevo cliente.domicilio ); scanf( "%ld",&nuevo cliente.no cuenta ); nuevo cliente.saldo += 10000.0; Por otra parte, el siguiente ejemplo muestra la inicializaci n completa de todos los campos de una o variable de tipo estructura en el momento de su declaraci n: o struct cliente nuevo cliente =

f
"Federico Sancho Buch", 23347098, "Rue del Percebe 13 - Madrid", 7897894, 1023459.34

g;

Finalmente, tambi n es posible copiar todos los datos de una variable estructura a otra variable e estructura del mismo tipo mediante una simple asignaci n: o nuevo cliente = antiguo cliente; No est permitido en ning n caso, comparar dos estructuras utilizando los operadores relacionales que a u vimos en la secci n 3.4.3. o

Los autores, 2000; Edicions UPC, 2000.

8.2. Uniones

66

8.1.4 Ejemplo
El siguiente programa dene las estructuras de datos para gestionar un conjunto de chas personales. N tese el uso de estructuras anidadas, as como el uso de tablas de estructuras. N tese tambi n el uso o o e de la funci n gets para leer cadenas de caracteres. Se deja como ejercicio para el lector escribir este o mismo programa usando scanf en lugar de gets para leer dichas cadenas. #include <stdio.h> struct datos char nombre[20]; char apellido[20]; long int dni;

g; f

struct tablapersonas int numpersonas; struct datos persona[100];

g; f

void main() int i; struct tablapersonas tabla; printf( "Nmero de personas: " ); u scanf( "%d", &tabla.numpersonas ); for (i= 0; i< tabla.numpersonas; i++)

printf( "nnNombre: " ); gets( tabla.persona[i].nombre ); printf( "nnApellido: " ); gets( tabla.persona[i].apellido ); printf( "nnDNI: " ); scanf( "%ld", &tabla.persona[i].dni );

8.2 Uniones
Al igual que las estructuras, las uniones tambi n pueden contener m ltiples campos de diferentes tipos e u de datos. Sin embargo, mientras que cada campo en una estructura posee su propia area de almacena miento, en una uni n, todos los campos que la componen se hallan almacenados en la misma area de o memoria. El espacio de memoria reservado por el compilador corresponde al del campo de la uni n o que requiere mayor espacio de almacenamiento. El resto de campos comparten dicho espacio. As pues, los campos de una uni n est n solapados en memoria, por lo que, en un momento dado de o a la ejecuci n del programa, s lo podr haber almacenado un dato en uno de los campos. Es responsabilio o a

Los autores, 2000; Edicions UPC, 2000.

67

8. Otros tipos de datos

dad del programador hacer que el programa mantenga control sobre qu campo contiene la informaci n e o almacenada en la uni n en cada momento. Intentar acceder al tipo de informaci n equivocado puede o o producir resultados sin sentido. La denici n de una uni n es muy similar a la de una estructura, excepto por el uso de la palabra o o reservada union: union nombre union

g;

tipo campo 1 nombre campo 1; tipo campo 2 nombre campo 2; . . . tipo campo N nombre campo N;

Tanto la declaraci n de variables de tipo uni n como el acceso a los campos se expresa de forma o o similar a como se mostr en las estructuras. o Finalmente, diremos que una estructura puede ser el campo de una uni n y viceversa. Igualmente, o pueden denirse tablas de uniones, etc.

8.2.1 Ejemplo
El siguiente ejemplo dene tres estructuras para almacenar la informaci n asociada a tres tipos difereno tes de m quinas voladoras (jet, helicoptero y carguero). Finalmente dene una estructura a gen rica que puede contener, alternativamente, los datos de cualquiera de ellos (un aeroplano). e Para controlar de qu tipo es el objeto almacenado en la uni n datos, se utiliza la variable tipo. e o

struct jet int num pasajeros; . . .

g; f

struct helicoptero int capacidad elevador; . . .

g; f

struct carguero int carga maxima; . . .

g; f

union aeroplano struct jet jet u; struct helicoptero helicopter u; struct carguero carguero u;

g;

Los autores, 2000; Edicions UPC, 2000.

8.3. Tipos de datos enumerados

68

struct un aeroplano

g;

/* 0:jet, 1:helicoptero, 2:carguero */ int tipo; union aeroplano datos;

8.3 Tipos de datos enumerados


Un objeto enumerado consiste en un conjunto ordenado de constantes enteras cuyos nombres indican todos los posibles valores que se le pueden asignar a dicho objeto. La denici n de una plantilla de un objeto enumerado requiere especicar un nombre mediante o el cual pueda identicarse, as como la lista de constantes de los posibles valores que puede tomar. Para ello se utiliza la palabra reservada enum de la forma siguiente: enum nombre enumeracin o

constante 1, constante 2, . . . constante N; g;

El siguiente ejemplo dene una enumeraci n denominada dia semana, cuyo signicado es obo vio. enum dia semana

LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO g;

Las constantes que denen los posibles valores de la enumeraci n son representadas internamente o como constantes enteras. El compilador asigna a cada una de ellas un valor en funci n del orden o o (empezando por 0) en que aparecen en la denici n. As pues, en el ejemplo anterior tenemos que e o LUNES es 0, MARTES es 1, etc. Sin embargo, podra ser de inter s modicar dicha asignaci n por defecto, asignando a las constantes otro valor num rico. Ver el siguiente ejemplo: e enum dia semana

LUNES=2, MARTES=3, MIERCOLES=4, JUEVES=5, VIERNES=6, SABADO=7, DOMINGO=1 g;

Tambi n puede asignarse el mismo valor num rico a diferentes constantes, as como dejar que el e e compilador numere algunas por defecto. Ver el siguiente ejemplo: enum dia semana

LUNES=1, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO=10, FESTIVO=10 g;

donde los valores asociados a las constantes son, respectivamente: 1, 2, 3, 4, 5, 6, 10 y 10.

Los autores, 2000; Edicions UPC, 2000.

69

8. Otros tipos de datos

La declaraci n de variables puede hacerse de dos formas diferentes. Bien en la misma denici n o o de la enumeraci n, bien posteriormente. Por su similitud con la declaraci n de variables en el caso de o o estructuras y uniones, veremos simplemente un ejemplo del segundo caso. El mismo c digo muestra o algunos ejemplos de utilizaci n de las constantes denidas en un tipo enumerado. o enum dia semana

LUNES=1, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO=10, FESTIVO=10 g;

void main() enum dia semana dia; . . . if (dia <= VIERNES) printf( "Es laborable" ); . . . dia = MARTES; /* Muestra el valor de dia */ printf( "Hoy es: %d", dia ); . . .

8.4 Denici n de nuevos tipos de datos o


C ofrece al programador la posibilidad de denir nuevos tipos de datos mediante la palabra reservada typedef. Los nuevos tipos denidos pueden utilizarse de la misma forma que los tipos de datos predenidos por el lenguaje. Es importante destacar que typedef tiene especial utilidad en la denici n o de nuevos tipos de datos estructurados, basados en tablas, estructuras, uniones o enumeraciones. La sintaxis general para denir un nuevo tipo de datos es la siguiente: typedef tipo de datos nombre nuevo tipo; De esta forma se ha denido un nuevo tipo de datos de nombre nombre nuevo tipo cuya estructura interna viene dada por tipo de datos. A continuaci n se muestran algunos ejemplos de denici n de nuevos tipos de datos, as como su o o utilizaci n en un programa: o

typedef float Testatura; typedef char Tstring [30]; typedef enum Tsexo =

HOMBRE, MUJER

g;

Los autores, 2000; Edicions UPC, 2000.

8.5. Tiras de bits

70

typedef struct

f g f

Tstring nombre, apellido; Testatura alt; Tsexo sex; Tpersona;

void main() Tpersona paciente; . . . scanf( "%f", &paciente.alt ); gets( paciente.nombre ); printf( "%s", paciente.apellido ); paciente.sex = MUJER;

8.5 Tiras de bits


C es un lenguaje muy vers til que permite programar con un alto nivel de abstraccion. Sin embarco, C a tambi n permite programar a muy bajo nivel. Esta caracterstica es especialmente util, por ejemplo, para e programas que manipulan dispositivos hardware, como el programa que controla el funcionamiento de un modem, etc. Este tipo de programas manipulan tiras de bits. En C, una tira de bits se debe almacenar como una variable entera con o sin signo. Es decir, como una variable de tipo char, short, int, long, unsigned, etc. Seguidamente veremos las operaciones que C ofrece para la manipulaci n tiras de bits. o

8.5.1 Operador de negaci n o


Este operador tambi n recibe el nombre de operador de complemento a 1, y se representa mediante el e smbolo . La funcion de este operador consiste en cambiar los 1 por 0 y viceversa. Por ejemplo, el siguiente programa: #include <stdio.h> void main()

unsigned short a; a= 0x5b3c; /* a = /* b = b= a; printf( " a= %x b= printf( " a= %u b= printf( " a= %d b= 0101 1011 1010 0100 %xnn", a, %unn", a, %dnn", a, 0011 1100 */ 1100 0011 */ b ); b ); b );

Los autores, 2000; Edicions UPC, 2000.

71

8. Otros tipos de datos

Tabla 8.1: Tabla de verdad de los operadores l gicos o x 0 0 1 1 y 0 1 0 1 AND (&) 0 0 0 1 OR ( j ) 0 1 1 1 XOR (b) 0 1 1 0

mostrara en pantalla los siguientes mensajes: a= 0x5b3c a= 23356 a= 23356 b= 0xa4c3 b= 42179 b= -23357

como resultado de intercambiar por sus complementarios los bits de la variable a.

8.5.2 Operadores l gicos o


C permite realizar las operaciones l gicas AND (&), OR ( j ) y XOR (b), tambi n llamadas respectivao e mente Y, O y O exclusivo. La tabla 8.1 muestra la tabla de verdad de estas operaciones, donde x e y son bits. Cuando aplicamos estos operadores a dos variables la operaci n l gica se realiza bit a bit. Ve moslo o o a en el siguiente ejemplo: a= b= c= d= e= 0x5b3c; 0xa4c3; a & b; a j b; a b b; /* /* /* /* /* a b c d e = = = = = 1101 1010 1000 1111 0111 1011 0101 0001 1111 1110 0001 1100 0000 1101 1101 1101 1011 1001 1111 0110 */ */ */ */ */

8.5.3 Operadores de desplazamiento de bits


Existen dos operadores de desplazamiento que permiten desplazar a derecha o izquierda una tira de bits. El operador de desplazamiento a la dercha se denota como >> y el operador de desplazamiento a la izquierda se denota como <<. Su uso es como sigue: var1 << var2 var1 >> var2 donde var1 es la tira de bits desplazada y var2 es el n mero de bits desplazados. En el desplazau miento a la izquierda se pierden los bits de m s a la izquierda de var1, y se introducen ceros por la a derecha. De forma similar, en el desplazamiento a la derecha se pierden los bits de m s a la derecha de a var1, y por la izquierda se introducen, o bien ceros (si la variable es de tipo unsigned, o bien se repite el ultimo bit (si la variable es de tipo signed). El siguiente ejemplo muestra el resultado de aplicar los operadores de desplazamiento de bits:

Los autores, 2000; Edicions UPC, 2000.

8.6. Ejercicios

72

unsigned short a, d, e; short b, c, f, g; a= b= c= d= e= f= g= 28; -28; 3; a << a >> b << b >> /* /* /* /* /* /* /* a b c d e f g = = = = = = = 0000 1111 0000 0000 0000 1111 1111 0000 1111 0000 0000 0000 1111 1111 0001 1110 0000 1110 0000 0010 1111 1100 0100 0011 0000 0011 0000 1100 */ */ */ = 224 */ = 3 */ = -224 */ = -3 */

c; c; c; c;

Es importante se alar que los operadores de desplazamiento tienen un signicado aritm tico en n e base 10. El desplazamiento a la derecha de 1 bit es equivalente a dividir por 2, obteni ndose el cociente e de la division entera. El desplazamiento a la izquierda de 1 bit es equivalente a multiplicar por 2. Por lo tanto, cuando trabajemos con variables enteras: var << n equivale a var * 2n , y var >> n equivale a var / 2n .

8.6 Ejercicios
1. Escribir un programa que, dadas dos fechas, indique cu l de las dos es anterior a la otra. Para a ello, el programa deber denir una estructura para el tipo de datos fecha (da, mes y a o). a n 2. Denir una estructura de datos para representar polinomios de hasta grado 10. Escribir un programa capaz de sumar dos polinomios expresados con dicha estructura. 3. Dada la siguiente denici n de tipos de datos: o typedef char Tstring [50]; typedef struct

f g

Tstring nombre; Tstring area; long int ventas[4]; Tagente;

Escribir un programa que dena un vector de N agentes, permita la introducci n de los datos por o teclado y, nalmente, muestre los datos del agente con la m xima media de ventas. a 4. Dada la siguiente denici n de tipo de datos: o typedef struct

f g

Tstring pais; int temp max; int temp min; Ttemp;

Los autores, 2000; Edicions UPC, 2000.

73

8. Otros tipos de datos

escribir un programa que dena un vector con los datos de N pases y permita introducirlos por teclado. Finalmente, el programa deber mostrar el nombre de los pases cuya temperatura a mnima sea inferior a la media de las temperaturas mnimas. 5. Dada la siguiente denici n de tipos de datos: o typedef char Tstring [50]; typedef struct

f g f

long int num; char letra; Tnif;

typedef struct Tnif nif; Tstring nombre; Tstring direc; long int telf; Tempresa;

escribir un programa que dena un vector con los datos de N empresas y permita introducirlos por teclado. Dado el NIF de una empresa, el programa deber permitir mostrar los datos de la a misma, as como eliminarla del vector, reorganiz ndolo para no dejar espacios vacos. a

Los autores, 2000; Edicions UPC, 2000.

75

9. Punteros

Captulo 9

Punteros
Un puntero es una variable que contiene como valor una direcci n de memoria. Dicha direcci n correso o ponde habitualmente a la direcci n que ocupa otra variable en memoria. Se dice entonces que el puntero o apunta a dicha variable. La variable apuntada puede ser de cualquier tipo elemental, estructurado o incluso otro puntero. Los punteros constituyen una parte fundamental del lenguaje C y son b sicos para comprender toda a la potencia y exibilidad que ofrece el lenguaje. Son especialmente importantes en la programaci n o a bajo nivel, donde se manipula directamente la memoria del ordenador. Algunas de las ventajas que aporta el uso de punteros en C son: Constituyen la unica forma de expresar algunas operaciones. Su uso produce c digo compacto y eciente. o Son imprescindibles para el paso de par metros por referencia a funciones. a Tienen una fuerte relaci n con el manejo eciente de tablas y estructuras. o Permiten realizar operaciones de asignaci n din mica de memoria y manipular estructuras de o a datos din micas. a Finalmente, cabe advertir al lector que los punteros son tradicionalmente la parte de C m s difcil de a comprender. Adem s deben usarse con gran precauci n, puesto que al permitir manipular directamente a o la memoria del ordenador, pueden provocar fallos en el programa. Estos fallos suelen ser bastante difciles de localizar y de solucionar.

9.1 Declaraci n y asignaci n de direcciones o o


En la declaraci n de punteros y la posterior asignaci n de direcciones de memoria a los mismos, se o o utilizan respectivamente los operadores unarios * y &. El operador & permite obtener la direcci n o que ocupa una variable en memoria. Por su parte, el operador de indirecci n * permite obtener el o contenido de un objeto apuntado por un puntero.

Los autores, 2000; Edicions UPC, 2000.

9.1. Declaraci n y asignaci n de direcciones o o

76

9.1.1 Declaraci n o
En la declaraci n de variables puntero se usa tambi n el operador * , que se aplica directamente a la o e variable a la cual precede. El formato para la declaraci n de variables puntero es el siguiente: o tipo de datos * nombre variable puntero; N tese que un puntero debe estar asociado a un tipo de datos determinado. Es decir, no puede asignarse o la direcci n de un short int a un puntero a long int. Por ejemplo: o int *ip; declara una variable de nombre ip que es un puntero a un objeto de tipo int. Es decir, ip contendr a direcciones de memoria donde se almacenaran valores enteros. No debe cometerse el error de declarar varios punteros utilizando un solo *. Por ejemplo: int *ip, x; declara la variable ip como un puntero a entero y la variable x como un entero (no un puntero a un entero). El tipo de datos utilizado en la declaraci n de un puntero debe ser del mismo tipo de datos que o las posibles variables a las que dicho puntero puede apuntar. Si el tipo de datos es void, se dene un puntero gen rico de forma que su tipo de datos implcito ser el de la variable cuya direcci n se le e a o asigne. Por ejemplo, en el siguiente c digo, ip es un puntero gen rico que a lo largo del programa o e apunta a objetos de tipos distintos, primero a un entero y posteriormente a un car cter. a void *ip; int x; char car; . . . ip = &x; ip = &car;

/* ip apunta a un entero */ /* ip apunta a un carcter */ a

Al igual que cualquier variable, al declarar un puntero, su valor no est denido, pues es el corresa pondiente al contenido aleatorio de la memoria en el momento de la declaraci n. Para evitar el uso o indebido de un puntero cuando a n no se le ha asignado una direcci n, es conveniente inicializarlo con u o el valor nulo NULL, denido en el chero de la librera est ndar stdio.h. El siguiente ejemplo a muestra dicha inicializaci n: o int *ip = NULL; De esta forma, si se intentase acceder al valor apuntado por el puntero ip antes de asignarle una direcci n v lida, se producira un error de ejecuci n que interrumpira el programa. o a o

9.1.2 Asignaci n de direcciones o


El operador & permite obtener la direcci n que ocupa una variable en memoria. Para que un puntero o apunte a una variable es necesario asignar la direcci n de dicha variable al puntero. El tipo de datos de o puntero debe coincidir con el tipo de datos de la variable apuntada (excepto en el caso de un puntero de tipo void). Para visualizar la direcci n de memoria contenida en un puntero, puede usarse el o modicador de formato %p con la funci n printf: o

Los autores, 2000; Edicions UPC, 2000.

77

9. Punteros

double num; double *pnum = NULL; . . . pnum = &num; printf( "La direccin contenida en pnum es: o

%p", pnum );

9.2 Indirecci n o
Llamaremos indirecci n a la forma de referenciar el valor de una variable a trav s de un puntero que o e apunta a dicha variable. En general usaremos el t rmino indirecci n para referirnos al hecho de ree o ferenciar el valor contenido en la posici n de memoria apuntada por un puntero. La indirecci n se o o realiza mediante el operador *, que precediendo al nombre de un puntero indica el valor de la variable cuya direcci n est contenida en dicho puntero. A continuaci n se muestra un ejemplo del uso de la o a o indirecci n: o int x, y; int *p; . . . x = 20; p = &x; *p = 5498; y = *p;

/* Se usa * para declarar un puntero */

/* Se usa * variable /* Se usa * variable

para indicar el valor de la apuntada */ para indicar el valor de la apuntada */

Despu s de ejecutar la sentencia *p = 5498; la variable x, apuntada por p , toma por valor 5498. e Finalmente, despu s de ejecutar la sentencia y = *p; la variable y toma por valor el de la variable e x, apuntada por p. Para concluir, existe tambi n la indirecci n m ltiple, en que un puntero contiene la direcci n de e o u o otro puntero que a su vez apunta a una variable. El formato de la declaraci n es el siguiente: o tipo de datos ** nombre variable puntero; En el siguiente ejemplo, pnum apunta a num, mientras que ppnum apunta a pnum. As pues, la sentencia **ppnum = 24; asigna 24 a la variable num. int num; int *pnum; int **ppnum; . . . pnum = &num; ppnum = &pnum; **ppnum = 24; printf( "%d", num );

Los autores, 2000; Edicions UPC, 2000.

9.3. Operaciones con punteros

78

La indirecci n m ltiple puede extenderse a m s de dos niveles, aunque no es recomendable por la o u a dicultad que supone en la legibilidad del c digo resultante. o La utilizaci n de punteros debe hacerse con gran precauci n, puesto que permiten manipular direco o tamente la memoria. Este hecho puede provocar fallos inesperados en el programa, que normalmente son difciles de localizar. Un error frecuente consiste en no asignar una direcci n v lida a un puntero o a antes de usarlo. Veamos el siguiente ejemplo: int *x; . . . *x = 100; El puntero x no ha sido inicializado por el programador, por lo tanto contiene una direcci n de memoria o aleatoria, con lo que es posible escribir involuntariamente en zonas de memoria que contengan c digo o o datos del programa. Este tipo de error no provoca ning n error de compilaci n, por lo que puede ser u o difcil de detectar. Para corregirlo es necesario que x apunte a una posici n controlada de memoria, o por ejemplo: int y; int *x; . . . x = &y; *x = 100;

9.3 Operaciones con punteros


En C pueden manipularse punteros mediante diversas operaciones como asignaci n, comparaci n y o o operaciones aritm ticas. e

Asignaci n de punteros o
Es posible asignar un puntero a otro puntero, siempre y cuando ambos apunten a un mismo tipo de datos. Despu s de una asignaci n de punteros, ambos apuntan a la misma variable, pues contienen la e o misma direcci n de memoria. En el siguiente ejemplo, el puntero p2 toma por valor la direcci n de o o memoria contenida en p1. As pues, tanto y como z toman el mismo valor, que corresponde al valor de la variable x, apuntada tanto por p1 como por p2. int x, y, z; int *p1, *p2; . . . x = 4; p1 = &x; p2 = p1; y = *p1; z = *p2;

Los autores, 2000; Edicions UPC, 2000.

79

9. Punteros

Comparaci n de punteros o
Normalmente se utiliza la comparaci n de punteros para conocer las posiciones relativas que ocupan o en memoria las variables apuntadas por los punteros. Por ejemplo, dadas las siguientes declaraciones, int *p1, *p2, precio, cantidad; *p1 = &precio; *p2 = &cantidad; la comparaci n p1 > p2 permite saber cu l de las dos variables (precio o cantidad) ocupa una o a posici n de memoria mayor. Es importante no confundir esta comparaci n con la de los valores de las o o variables apuntadas, es decir, *p1 > *p2 .

Aritm tica de punteros e


Si se suma o resta un n mero entero a un puntero, lo que se produce implcitamente es un incremento u o decremento de la direcci n de memoria contenida por dicho puntero. El n mero de posiciones de o u memoria incrementadas o decrementadas depende, no s lo del n mero sumado o restado, sino tambi n o u e del tama o del tipo de datos apuntado por el puntero. Es decir, una sentencia como: n nombre puntero = nombre puntero + N; se interpreta internamente como: nombre puntero = direccin + N * tamao tipo de datos; o n Por ejemplo, teniendo en cuenta que el tama o de un float es de 4 bytes, y que la variable n a o num se sit a en la direcci n de memoria 2088, cu l es el valor de pnum al nal del siguiente c digo? u o float num, *punt, *pnum; . . . punt = &num; pnum = punt + 3; La respuesta es 2100. Es decir, 2088 + 3
4

Es importante advertir que aunque C permite utilizar aritm tica de punteros, esto constituye una e pr ctica no recomemdable. Las expresiones aritm ticas que manejan punteros son difciles de entender a e y generan confusi n, por ello son una fuente inagotable de errores en los programas. Como veremos o en la siguiente secci n, no es necesario usar expresiones aritm ticas con punteros, pues C proporciona o e una notaci n alternativa mucho m s clara. o a

9.4 Punteros y tablas


En el lenguaje C existe una fuerte relaci n entre los punteros y las estructuras de datos de tipo tabla o (vectores, matrices, etc.). En C, el nombre de una tabla se trata internamente como un puntero que contiene la direcci n del o primer elemento de dicha tabla. De hecho, el nombre de la tabla es una constante de tipo puntero, por

Los autores, 2000; Edicions UPC, 2000.

9.4. Punteros y tablas

80

lo que el compilador no permitir que las instrucciones del programa modiquen la direcci n contenida a o en dicho nombre. As pues, dada una declaraci n como char tab[15] , el empleo de tab es o equivalente al empleo de &tab[0]. Por otro lado, la operaci n tab = tab + 1 generar un error o a de compilaci n, pues representa un intento de modicar la direcci n del primer elemento de la tabla. o o C permite el uso de punteros que contengan direcciones de los elementos de una tabla para acceder a ellos. En el siguiente ejemplo, se usa el puntero ptab para asignar a car el tercer elemento de tab, leer de teclado el quinto elemento de tab y escribir por pantalla el d cimo elemento del vector e tab. char car; char tab[15]; char *ptab; . . . ptab = &tab; ptab = ptab + 3; car = *(ptab); /* Equivale a car = tab[3]; */ scanf( "%c", ptab+5 ); printf( "%c", ptab+10 ); Pero la relaci n entre punteros y tablas en C va a n m s all . Una vez declarado un puntero que o u a a apunta a los elementos de una tabla, pueden usarse los corchetes para indexar dichos elementos, como si de una tabla se tratase. As, siguiendo con el ejemplo anterior, sera correcto escribir: scanf( "%c", ptab[0] );/* ptab[0] equivale a tab[0] */ ptab[7] = R; /* ptab[7] equivale a *(ptab +7) */ Por lo tanto, vemos que no es necesario usar expresiones aritm ticas con punteros; en su lugar e usaremos la sintaxis de acceso a una tabla. Es importante subrayar que este tipo de indexaciones s lo o son v lidas si el puntero utilizado apunta a los elementos de una tabla. Adem s, no existe ning n tipo a a u de vericaci n al respecto, por lo que es responsabilidad del programador saber en todo momento si o est accediendo a una posici n de memoria dentro de una tabla o ha ido a parar fuera de ella y est a o a sobreescribiendo otras posiciones de memoria. El siguiente ejemplo muestra el uso de los punteros a tablas para determinar cu l de entre dos veca tores de enteros es m s fuerte. La fuerza de un vector se calcula como la suma de todos sus elementos. a #include <stdio.h> #define DIM 10 void main()

int v1[DIM], v2[DIM]; int i, fuerza1, fuerza2; int *fuerte; /* Lectura de los vectores. for (i= 0; i< DIM; i++) scanf( "%d ", &v1[i] ); */

Los autores, 2000; Edicions UPC, 2000.

81

9. Punteros

for (i= 0; i< DIM; i++) scanf( "%d ", &v2[i] ); /* Clculo de la fuerza de los vectores. a fuerza1 = 0; fuerza2 = 0; for (i= 0; i< DIM; i++) */

f g

fuerza1 = fuerza1 + v1[i]; fuerza2 = fuerza2 + v2[i];

if (fuerza1 > fuerza2) fuerte = v1; else fuerte = v2; /* Escritura del vector ms fuerte. a for (i= 0; i< DIM; i++) printf( "%d ", fuerte[i] ); */

En el caso de usar punteros para manipular tablas multidimensionales, es necesario usar f rmulas o de transformaci n para el acceso a los elementos. Por ejemplo, en el caso de una matriz de n las y o m columnas, el elemento que ocupa la la i y la columna j se referencia por medio de un puntero como puntero[i*m+j]. En el siguiente ejemplo se muestra el acceso a una matriz mediante un puntero. float mat[3][5]; float *pt; pt = mat; pt[i*5+j] = 4.6; /* Equivale a mat[i][j]=4.6 */

Cuando usamos el puntero para acceder a la matriz, la expresi n pt[k] signica acceder al elemento o de tipo float que est k elementos por debajo del elemento apuntado por pt . Dado que en C las a matrices se almacenan por las, para acceder al elemento de la la i columna j deberemos contar o cuantos elementos hay entre el primer elemento de la matriz y el elemento [i][j ]. Como la numeraci n comienza en cero, antes de la la i hay exactamente i las, y cada una tiene m columnas. Por lo tanto hasta el primer elemento de la la i tenemos i m elementos. Dentro de la la i, por delante del elemento j , hay j elementos. Por lo tanto tenemos que entre mat[0][0] y mat[i][j] hay i m j elementos. La gura 9.1 muestra como est dispuesta en memoria la matriz de este ejemplo a y una explicaci n gr ca del c lculo descrito. o a a

Los autores, 2000; Edicions UPC, 2000.

9.4. Punteros y tablas

82

Puntero pt A[0][0] Fila 0

...

i*m elementos

A[0][m-1]

A[i][0]

...

i*m+j elementos

Fila i

A[i][j]

A[i][m-1]

A[n-1][0] Fila n-1

A[n-1][m-1]

Figura 9.1: Acceso a una matriz mediante un puntero

Punteros y cadenas de caracteres


Una cadena de caracteres puede declararse e inicializarse sin necesidad de especicar explcitamente su longitud. La siguiente declaraci n es un ejemplo: o char mensaje[] = "Reiniciando sistema"; La cadena de caracteres mensaje ocupa en memoria 20 bytes (19 m s el car cter nulo n0). El a a nombre mensaje corresponde a la direcci n del primer car cter de la cadena. En esta declaraci n, o a o mensaje es un puntero constante, por lo que no puede modicarse para que apunte a otro car cter disa tinto del primer car cter de la cadena. Sin embargo, puede usarse mensaje para acceder a caracteres a individuales dentro de la cadena, mediante sentencias como mensaje[13]=S, etc. La misma declaraci n puede hacerse usando un puntero: o char *pmens = "Reiniciando sistema"; En esta declaraci n, pmens es una variable puntero inicializada para apuntar a una cadena constante, o que como tal no puede modicarse. Sin embargo, el puntero pmens puede volver a utilizarse para apuntar a otra cadena.

...

...

...

...

j elementos

Los autores, 2000; Edicions UPC, 2000.

83

9. Punteros

Puede decirse que en general a una tabla puede accederse tanto con ndices como con punteros (usando la aritm tica de punteros). Habitualmente es m s c modo el uso de ndices, sin embargo en el e a o paso de par metros a una funci n (ver Cap. 10) donde se recibe la direcci n de una tabla, es necesario a o o utilizar punteros. Como veremos, dichos punteros podr n usarse como tales o usando la indexaci n a o tpica de las tablas.

9.5 Punteros y estructuras


Los punteros a estructuras funcionan de forma similar a los punteros a variables de tipos elementales, con la salvedad de que en las estructuras se emplea un operador especco para el acceso a los campos. Puede accederse a la direcci n de comienzo de una variable estructura en memoria mediante el o empleo del operador de direcci n &. As pues, si var es una variable estructura, entonces &var reo presenta la direcci n de comienzo de dicha variable en memoria. Del mismo modo, puede declararse o una variable puntero a una estructura como: tipo estructura *pvar; donde tipo estructura es el tipo de datos de las estructuras a las que pvar puede apuntar. As pues, puede asignarse la direcci n de comienzo de una variable estructura al puntero de la forma o habitual: pvar = &var; Veamos un ejemplo. A continuaci n se dene, entre otros, el tipo de datos Tnif como: o typedef char Tstring [50];

f g f

typedef struct long int num; char letra; Tnif;

typedef struct Tnif nif; Tstring nombre; Tstring direc; long int telf; Tempresa;

De forma que en el programa puede declararse una variable cliente de tipo Tnif y un puntero pc a dicho tipo, as como asignar a pc la direcci n de inicio de la variable estructura cliente : o Tnif cliente; Tnif *pc; . . . pc = &cliente;

Los autores, 2000; Edicions UPC, 2000.

9.6. Ejercicios

84

Para acceder a un campo individual en una estructura apuntada por un puntero puede usarse el operador de indirecci n * junto con el operador punto (.) habitual de las estructuras. Vemos un o ejemplo: (*pc).letra = Z; scanf( "%ld", &(*pc).num ); Los par ntesis son necesarios ya que el operador punto (.) tiene m s prioridad que el operador *. e a N tese que el operador de direcci n & en la llamada a scanf se reere al campo num y no al o o puntero pc. El hecho de que sea obligatorio usar par ntesis en esta notaci n hace que se generen errores debido e o al olvido de los par ntesis adecuados. Para evitar estos errores, C posee otra notaci n para acceder a e o los campos de una estructura apuntada por un puntero. El acceso a campos individuales puede hacerse tambi n mediante el operador especial de los pune teros a estructuras -> (gui n seguido del smbolo de mayor que). As pues, puede escribirse el mismo o ejemplo anterior como: pc->letra = Z; scanf( "%ld", &pc->num ); El operador -> tiene la misma prioridad que el operador punto (.). Es recomendable usar el operador -> al manejar structs apuntados por punteros. El acceso a los campos de una estructura anidada a partir de un puntero puede hacerse combinando los operadores de punteros con el punto (.). Veamos el siguiente ejemplo: Tempresa emp; Tempresa *pe; char inicial; . . . pe = &emp; pe->nif.letra = Z; scanf( "%ld", &pe->nif.num ); gets( pe->nombre ); inicial = pe->nombre[0]; . . .

9.6 Ejercicios
1. Escribir un programa que pase a may sculas la inicial de todas las palabras en una cadena de u caracteres. Usar un puntero a dicha cadena para acceder a los elementos de la misma. 2. Escribir un programa que calcule el m ximo de un vector de n meros enteros, utilizando un a u puntero para acceder a los elementos del vector. 3. Escribir un programa que, usando punteros, copie en orden inverso una cadena de caracteres en otra.

Los autores, 2000; Edicions UPC, 2000.

85

9. Punteros

4. Un programa en C contiene las siguientes instrucciones: char u, v; char *pu, *pv; . . . v = A; pv = &v; *pv = v + 1; u = *pv + 1; pu = &u; Si cada car cter ocupa 1 byte de memoria y la variable u se sit a en la direcci n FC8 (hexadea u o cimal), responder a las siguientes preguntas: (a) Qu valor representa &v ? e (b) Qu valor se asigna a pv ? e (c) Qu valor representa *pv ? e (d) Qu valor se asigna a u ? e (e) Qu valor representa &u ? e (f) Qu valor se asigna a pu ? e (g) Qu valor representa *pu ? e 5. Un programa en C contiene las siguientes instrucciones: float a = 0.1, b = 0.2; float c, *pa, *pb; . . . pa = &a; *pa = 2 * a; pb = &b; c = 5 * (*pb - *pa); Si cada variable de tipo float ocupa 4 bytes de memoria y la variable a se sit a en la u direcci n 1230 (hexadecimal), responder a las siguientes preguntas: o (a) Qu valor representa &a ? e (b) Qu valor representa &c ? e (c) Qu valor representa &pb ? e (d) Qu valor se asigna a pa ? e (e) Qu valor representa *pa ? e (f) Qu valor representa &(*pa) ? e (g) Qu valor se asigna a pb ? e (h) Qu valor representa *pb ? e

Los autores, 2000; Edicions UPC, 2000.

9.6. Ejercicios

86

(i) Qu valor se asigna a c ? e 6. Un programa de C contiene las siguientes sentencias: int i, j = 25; int *pi, *pj = &j; . . . *pj = j + 5; i = *pj + 5; pi = pj; *pi = i + j; Suponiendo que cada variable entera ocupa 2 bytes de memoria. Si la variable i se sit a en la u o direcci n 1000 y la variable j en la direcci n 1002, entonces: o (a) Qu valor representan &i y por &j ? e (b) Qu valor se asigna a pj , *pj y a i ? e (c) Qu valor representa pi ? e (d) Qu valor se asigna a *pi ? e (e) Qu valor representa pi + 2 ? e (f) Qu valor representa la expresi n (*pi + 2) ? e o (g) Qu valor representa la expresi n *(pi + 2) ? e o

Los autores, 2000; Edicions UPC, 2000.

87

10. Funciones

Captulo 10

Funciones
Hasta ahora hemos visto como el programa principal (main( )) utiliza funciones de la librera est ndar a de C para realizar algunas tareas comunes (printf( ), scanf( ), . . . ). C permite tambi n la dee nici n de funciones por parte del programador. Como veremos, al usar funciones denidas por el o programador, los programas pueden estructurarse en partes m s peque as y sencillas. Cada una de esa n tas partes debe responder a un prop sito unico e identicable, pudiendo adem s utilizarse en distintos o a lugares del programa. La distribuci n del c digo de un programa usando funciones se conoce como o o modularizaci n. o El dise o modular de programas ofrece diversas ventajas. Por ejemplo, muchos programas requien ren la ejecuci n de un mismo grupo de instrucciones en distintas partes del programa. Este grupo de o instrucciones puede incluirse dentro de una sola funci n, a la que se puede llamar cuando sea necesario. o Adem s, puede proporcionarse un conjunto de datos (par metros) diferente a la funci n cada vez que a a o se la llama. Es importante tambi n la claridad en la l gica del programa resultante de la descomposici n del e o o mismo en partes bien denidas. Un programa concebido de esta forma es mucho m s f cil de escribir y a a de depurar, no s lo por el propio programador, sino tambi n (y m s importante) por otros programadoo e a res que posteriormente deban mantener el programa. Este hecho es especialmente cierto en programas grandes donde participan muchos programadores. La utilizaci n de funciones permite tambi n la construcci n a medida de libreras de funciones de o e o uso frecuente. Por ejemplo, un programador especializado en el desarrollo de programas matem ticos a podra crear una librera con funciones para el manejo de matrices, n meros complejos, etc. De esta u forma se evita la reescritura del mismo c digo repetidas veces para distintos programas. o

10.1 Generalidades
Una funci n es una porci n de programa, identicable mediante un nombre, que realiza determinadas o o tareas bien denidas por un grupo de sentencias sobre un conjunto de datos. Las operaciones que realiza la funci n son siempre las mismas, pero los datos pueden variar cada vez que se llame a la funci n. o o Todo programa en C consta de una o m s funciones, una (y s lo una) de la cuales debe llamarse a o main. La ejecuci n del programa comienza siempre en dicha funci n, desde donde puede llamarse o o a otras funciones de la librera est ndar o denidas por el programador. Al llamar a una funci n se a o

Los autores, 2000; Edicions UPC, 2000.

10.1. Generalidades

88

ejecutan las sentencias que la componen y, una vez completada la ejecuci n de la funci n, la ejecuci n o o o del programa contin a desde el punto en que se hizo la llamada a la funci n. u o Generalmente, al llamar a una funci n se le proporciona un conjunto de datos (par metros) que o a se procesan ejecutando las sentencias que la componen. La funci n devuelve un solo valor mediante o la sentencia return. Algunas funciones reciben par metros, pero no devuelven nada (como la funa ci n printf), mientras que otras no reciben par metros pero s devuelven un valor (como la funci n o a o rand). El programa del siguiente ejemplo calcula el n mero combinatorio u

m n

m n m;n
! ! (

)!

, donde

es necesario calcular el factorial de tres n mero diferentes. Una posible realizaci n de este programa, u o si no se tuviese en cuenta el uso de funciones, sera la siguiente: #include <stdio.h> void main() long int m, n, fm = 1, fn = 1, fdif = 1; float res; int i; printf( "Introduzca m y n: " ); scanf( "%d %d", &m, &n ); for (i= 2; i<= m; i++) fm = fm * i; for (i= 2; i<= n; i++) fn = fn * i; for (i= 2; i<= m-n; i++) fdif = fdif * i; res = (float) fm / ((float)fn* (float)fdif); printf( "m sobre n = %fnn", res );

Como puede verse, el c digo para el c lculo del factorial se halla triplicado. Una soluci n m s clara y o a o a elegante puede obtenerse usando funciones: #include <stdio.h> long int fact ( int x )

int i; long int f = 1; for (i= 2; i<= x; i++) f = f * i; return(f);

void main()

Los autores, 2000; Edicions UPC, 2000.

89

10. Funciones

f
long int m, n; float res; printf( "Introduzca m y n: " ); scanf( "%d %d", &m, &n ); res = (float) fact(m) / ((float)fact(n)*(float)fact(m-n)); printf( "m sobre n = %fnn", res );

En el ejemplo se ha denido la funci n fact, que recibe como par metro un valor de tipo int, al que o a se ha llamado x, y devuelve un resultado de tipo long int. El c digo en el interior de la funci n o o calcula el factorial de x acumul ndolo sobre la variable local f. Finalmente la funci n devuelve el a o resultado del c lculo mediante la sentencia return. Obs rvese que la denici n de una funci n se a e o o asemeja a la del programa principal. De hecho, el programa principal main es una funci n. o Seguidamente veremos m s formalmente c mo denir funciones, c mo llamarlas, las distintas vaa o o riantes del paso de par metros, etc. a

10.2 Denici n y llamada o


10.2.1 Denici n o
La denici n de una funci n se hace de forma similar a la de la funci n main. Su forma m s gen rica o o o a e consta b sicamente de dos partes: un lnea llamada cabecera donde se especica el nombre de la a funci n, el tipo del resultado que devuelve y los par metros que recibe; y un conjunto de sentencias o a encerrado entre llaves formando el cuerpo. tipo nombre funcin(tipo1 param1, ..., tipoN paramN) o

f g

cuerpo

tipo: es el tipo de datos del valor que devuelve la funci n. Si no se especica ninguno, C asume o que la funci n devuelve un valor de tipo entero. o nombre funcin: identicador que se usar posteriormente para llamar a la funci n. o a o tipoi parami: tipo y nombre de cada uno de los par metros que recibe la funci n. Se espea o cican entre par ntesis y separados por comas. Algunas funciones pueden no tener par metros. e a Los par metros de la declaraci n se denominan par metros formales, ya que representan los a o a nombres con que referirse dentro de la funci n a los datos que se transeren a esta desde la parte o del programa que hace la llamada. cuerpo: conjunto de declaraci n de variables y sentencias de ejecuci n (incluyendo llamadas a o o funciones) necesarias para la realizaci n de la tarea especicada por la funci n. Debe incluir una o o o m s sentencias return para devolver un valor al punto de llamada. a

Los autores, 2000; Edicions UPC, 2000.

10.2. Denici n y llamada o

90

10.2.2 Prototipos
Si en el punto del programa donde se va a realizar una llamada a una funci n, dicha funci n ya ha sido o o denida previamente, entonces ya se conocen sus caractersticas (tipo del resultado, n mero y tipo de u los par metros, etc.), por lo que la llamada puede realizarse sin problemas. Sin embargo, si la funci n a o que se va a llamar se halla denida posteriormente al punto desde donde se realiza la llamada, entonces debe crearse un prototipo de la funci n a la cual se desea llamar. Dicho prototipo deber colocarse o a antes del punto donde se haga la primera llamada a la funci n, y consta unicamente de la cabecera de o dicha funci n. o El prototipo de una funci n puede interpretarse como un aviso al compilador, para que cuando o encuentre una llamada a dicha funci n pueda conocer el tipo del resultado que devuelve y la informaci n o o sobre los par metros que recibe. a A continuaci n se muestra el formato general de un prototipo, que corresponde a la cabecera de la o funci n seguida de un punto y coma: o tipo nombre funcin(tipo1 param1, ..., tipoN paramN); o De acuerdo con esto, el programa del ejemplo anterior podra haberse escrito de otro modo, de niendo la funci n fact con posterioridad a main y usando un prototipo: o #include <stdio.h> long int fact ( int x ); /* Prototipo */

void main() long int m, n; float res; printf( "Introduzca m y n: " ); scanf( "%d %d", &m, &n ); res = (float) fact(m) / ((float)fact(n)*(float)fact(m-n)); printf( "m sobre n = %fnn", res );

g f

long int fact ( int x ) int i; long int f = 1; for (i= 2; i<= x; i++) f = f * i; return(f);

La utilizaci n de prototipos de funciones no es obligatorio en C. Sin embargo, es aconsejable, ya o que facilitan la comprobaci n y detecci n de errores entre las llamadas a funciones y las deniciones o o correspondientes.

Los autores, 2000; Edicions UPC, 2000.

91

10. Funciones

10.2.3 Llamada
Finalmente, la llamada a una funci n se realiza con el nombre de la misma y una lista de par metros o a (si es que los requiere) entre par ntesis. El n mero y tipo de los par metros empleados en la llamada a e u a la funci n debe coincidir con el n mero y tipo de los par metros formales de la denici n o prototipo. o u a o Adicionalmente, si la funci n devuelve alg n valor (es decir, no es de tipo void) la llamada a la o u funci n debe estar incluida en una expresi n que recoja el valor devuelto. Siguiendo con el ejemplo del o o factorial: fm = fact(m); prod = fact(n)*fact(m-n); Los datos empleados en la llamada a una funci n reciben el nombre de par metros reales, ya que o a se reeren a la informaci n que se transere a la funci n para que esta se ejecute. Como veremos m s o o a adelante, los identicadores de los par metros formales son locales a la funci n, en el sentido de que a o no son reconocidos desde fuera de esta. Por tanto, los nombres de los par metros formales no tienen a por qu coincidir con los nombres de la variables usadas como par metros reales en el momento de la e a llamada.

10.3 Variables y par metros a


Las variables de un programa pueden clasicarse en funci n del ambito en el cual son conocidas y por o tanto accesibles. El ambito de las variables, as como su tiempo de vida, depende del lugar donde se hallen declaradas dentro del programa. As pues, se distinguen los siguientes tipos: variables locales, variables globales y par metros formales. a

10.3.1 Variables locales


Una variable local se halla declarada al comienzo del cuerpo de una funci n (esto incluye a la funci n o o main). Su ambito se circunscribe al bloque de sentencias que componen el cuerpo de la funci n, por o lo que s lo son conocidas dentro de el. Por otra parte, su tiempo de vida va desde que se entra en la o funci n hasta que se sale de ella, por lo que las variables locales se crean al comenzar la ejecuci n de o o la funci n y se destruyen al concluir dicha ejecuci n. o o En el ejemplo del c lculo de a

m n

, las variables i y f son locales a la funci n fact, por o

lo que no son accesibles fuera de ella. De forma similar, las variables m, n y res son locales a la funci n main. o

10.3.2 Variables globales


Una variable global se halla declarada fuera de toda funci n del programa al principio del chero o principal. Su ambito se extiende a lo largo de todas las funciones del programa. Su tiempo de vida est a limitado por el tiempo que dura la ejecuci n del programa, por lo que las variables globales se crean al o comenzar a ejecutar el programa y se destruyen cuando este concluye la ejecuci n. o Si todas las funciones del programa est n incluidas en un mismo chero, basta con escribir una a sola vez al principio del chero la declaraci n de las variables globales. Sin embargo, si tenemos las o

Los autores, 2000; Edicions UPC, 2000.

10.3. Variables y par metros a

92

funciones repertidas en diferentes cheros, deberemos incluir en uno de los cheros la declaraci n y o repetir en los dem s cheros la declaraci n precedida de la palabra extern. Tambi n es posible a o e denir variables globales unicamente dentro de un chero. Para ello antepondremos en la declaraci n o la palabra static. El siguiente ejemplo muestra las declaraciones de tres variables globales. La variable A es accesible en todo el programa y est declarada en este chero. La variable B es accesible en todo el programa, a pero est declarada en otro chero, es decir, hay otras funciones adem s de las de este chero que a a pueden acceder a ella. Finalmente, la variable C s lo es accesible por las funciones de este chero. o int A; extern int B; static int C; void main ()

f g f g f g

int func1() . . .

int func2() . . .

El uso de variables globales debe hacerse con precauci n, puesto que al poderse modicar desde o cualquier funci n del programa donde sean accesibles, pueden producirse efectos laterales difciles de o detectar y corregir. Es una buena pr ctica de programaci n no emplear variables globales salvo en a o casos muy excepcionales. En general, el uso de una variable global puede substituirse por una variable local al programa principal y el adecuado paso de par metros a todas las funciones que requieran a acceder a dicha variable.

10.3.3 Par metros formales a


El ambito y el tiempo de vida de un par metro formal en una funci n son los mismos que los de una a o variable local a dicha funci n. Es decir, que el ambito es toda la funci n y que la variable se crea al o o entrar en la funci n y se destruye al salir de la misma. o Como hemos comentado, el ambito de las variables locales y los par metros de una funci n se a o circunscribe unicamente al interior de la misma. Es decir, que ni las variables ni los par metros formales a de una funci n son accesibles desde fuera de ella. Incluso en el caso de que el programa use el mismo o nombre para variables de distintas funciones, el compilador es capaz de diferenciarlas. Para demostrar esto usaremos el operador de direcci n & en un ejemplo muy sencillo: o

Los autores, 2000; Edicions UPC, 2000.

93

10. Funciones

void func( int par )

f g f

int loc = 10; printf( "En func(), loc=%d y &loc=%pnn", loc, &loc ); printf( "En func(), par=%d y &par=%pnn", par, &par );

void main() int loc = 24, par = 5; printf( "En main(), loc=%d y &loc=%pnn", loc, &loc ); printf( "En main(), par=%d y &par=%pnn", par, &par ); func(loc);

Al compilar y ejecutar este ejemplo, observaremos que las variables loc y par se sit an en direcu ciones de memoria diferentes (y por tanto, son variables diferentes) si estamos dentro de la funci n o o en el programa principal.

10.4 Devoluci n de resultados o


Cuando una funci n termina de realizar la tarea para la que fue dise ada, devuelve el control de la o n ejecuci n a la parte del programa desde donde se hizo la llamada. Para concluir la ejecuci n de una o o funci n se utiliza la sentencia return, que fuerza la salida de la funci n en el punto donde se ha o o especicado dicha sentencia. Si no existe ninguna sentencia return, la funci n termina con la llave o que cierra el cuerpo. El siguiente ejemplo muestra dos maneras de escribir la misma funci n para el c lculo del m ximo o a a de dos n meros enteros: u void maximo( int x, int y )

void maximo( int x, int y )

f f

int max; if (x > y) max = x; else max = y; printf( "MAX=%d", max );

if (x > y) printf( "MAX=%d", x ); return;

g g

printf( "MAX=%d", y );

Si la funci n no es de tipo void, la sentencia return, adem s de especicar la terminaci n o a o de la funci n, puede especicar la devoluci n de un valor para que pueda ser utilizado en el punto o o donde se hizo la llamada. El valor devuelto, y por ende el tipo de la funci n, puede ser cualquiera de o los tipos elementales, estructurados o denidos por el programador, excepto tablas. Aunque en C es legal que una funci n retorne cualquier tipo de estructura, por cuestiones de eciencia en la ejecuci n o o del programa no es conveniente retornar estructuras muy grandes en tama o. Por ejemplo, estructuras n en cuyo interior haya tablas. Cuando se desea que una funci n devuelva tipos de datos complejos, se o utiliza el paso de par metros por referencia que veremos en la secci n 10.5. a o

Los autores, 2000; Edicions UPC, 2000.

10.5. Paso de par metros a

94

En el siguiente programa se ha rescrito la funci n maximo para que devuelva el m ximo de dos o a enteros, en lugar de mostrarlo por pantalla.

int maximo( int x, int y ) if (x > y) return(x); else return(y);

En la parte del programa que hace la llamada puede usarse el valor devuelto por la funci n dentro de o cualquier expresi n v lida (en particular una sentencia de escritura): o a printf( "MAX(%d,%d)=%d", a, b, maximo(a,b) );

10.5 Paso de par metros a


Como ya hemos comentado, los par metros de una funci n no son m s que variables que act an de a o a u enlace entre la parte del programa donde se realiza la llamada y el cuerpo de la funci n. As pues, o los par metros formales de una funci n son variables locales a esta. Como tales, se crean y reciben a o sus valores al entrar en la funci n, y se destruyen al salir de la misma. El paso de par metros puede o a realizarse de dos formas: por valor o por referencia.

10.5.1 Paso de par metros por valor a


Al entrar a la funci n, los par metros formales reciben una copia del valor de los par metros reales. Por o a a tanto las modicaciones sobre los par metros formales son locales y no afectan a los par metros reales a a de la parte del programa que hace la llamada a la funci n. Veamos un ejemplo: o #include <stdio.h> void cuadrado ( int x )

f g f

x = x * x; printf( "Dentro x = %dnn", x );

void main() int x = 5; printf( "Antes x = %dnn", x ); cuadrado( x ); printf( "Despus x = %dnn", x ); e

El resultado de ejecutarlo ser : a

Los autores, 2000; Edicions UPC, 2000.

95

10. Funciones

Antes x = 5 Dentro x = 25 Despus x = 5 e Como puede verse, la modicaci n sobre el par metro formal no afecta a la variable del programa o a principal. En el paso de par metros por valor, la transferencia de informaci n es s lo en un sentido, es decir, a o o desde la parte del programa donde se hace la llamada hacia el interior de la funci n, pero no al rev s. o e Gracias a ello, es posible utilizar expresiones como par metros reales, en lugar de necesariamente a variables. Esto es as puesto que el valor asignado al par metro formal es el resultado de la evaluaci n a o de dicha expresi n. Es m s, si el par metro real es una variable, su valor es protegido de posibles o a a modicaciones por parte de la funci n. o

10.5.2 Paso de par metros por referencia a


Hasta este punto hemos visto c mo proporcionar datos a una funci n (paso de par metros por valor) o o a o c mo hacer que la funci n devuelva resultados con la sentencia return. Sin embargo, c mo o o o podramos hacer que una funci n devolviese m s de un valor? O bien, c mo podramos conseguir que o a o las modicaciones sobre los par metros formales afectasen tambi n a los par metros reales? Es decir, a e a c mo podramos modicar variables del ambito en el que se realiz la llamada desde el interior de una o o funci n? o La respuesta a estas cuestiones se halla en la utilizaci n del paso de par metros por referencia. Este o a tipo de paso de par metros se conoce tambi n en C como paso por direcci n o paso por puntero. a e o En este caso los par metros reales son una referencia (puntero) a las variables de la parte del proa grama que realiza la llamada y no las variables en s. La referencia se copia en los par metros formales, a de forma que dentro de la funci n puede usarse dicha referencia para modicar una variable que, de o otra forma, no sera accesible desde el interior de la funci n (modicar el valor referenciado). o El paso de parametros por referencia implica el uso de los operadores de direcci n ( & ) y puntero o ( * ) de C: & , que antepuesto a una variable permite obtener la direcci n de memoria en que se halla ubicada. o Se usar en los par metros reales de la llamada a una funci n para pasarle por referencia dicha a a o variable. En otras palabras, el par metro que se pasa a la funci n es un puntero a una variable. a o Ya hemos utilizado esto anteriormente en las llamadas a la funci n scanf, donde las variables o en que se almacenan los valores ledos del teclado se pasan por referencia. * , que se utiliza tanto en la declaraci n de los par metros formales de la funci n como en el o a o cuerpo de la misma. Aparecer precediendo al nombre de un par metro formal en la cabecera a a para indicar que dicho par metro ser pasado por referencia (ser un puntero). Aparecer en el a a a a cuerpo de la funci n, antepuesto al nombre de un par metro formal, para acceder al valor de la o a variable externa a la funci n y referenciada por el par metro formal. o a En el siguiente ejemplo se muestra la funci n swap que intercambia el valor de sus dos par metros. o a Para ello los par metros formales x e y se han declarado de paso por referencia usando * en la a cabecera. De forma complementaria, en la llamada a la funci n se ha usado & para pasar a la funci n o o una referencia a las variables a y b del programa principal. Dentro de la funci n usamos nuevamente o

Los autores, 2000; Edicions UPC, 2000.

10.5. Paso de par metros a

96

* para referenciar el valor del par metro real. Por ejemplo, la sentencia aux = *x; asigna a la a variable local aux el valor de la variable a del programa principal, puesto que el par metro formal a x contiene una referencia a la variable a. #include <stdio.h> void swap(int *x, int *y) int aux; /* Se asigna a aux el valor referenciado por x */ aux = *x; /* Se asigna el valor referenciado por y al valor referenciado por x */ *x = *y; /* El valor referenciado por y pasa a ser el valor de aux */ *y = aux;

g f

void main() int a, b; scanf( "%d %d", &a, &b ); swap( &a, &b ); printf( "Los nuevos valores son a=%d y b=%dnn", a, b );

Como puede verse, el paso de par metros por referencia tiene una estrecha relaci n con el uso de a o punteros y direcciones de memoria que vimos en el captulo 9. De hecho un purista del lenguaje dira que en C no existe el paso por referencia y que todo paso de par metros se hace por valor. El paso por a referencia se simula mediante el paso (por valor) de punteros a las variables externas a la funci n y o que se desean modicar desde el interior de la misma.

10.5.3 Las tablas y las funciones


Los elementos individuales de una tabla (vector, matriz, etc.) se pasan a las funciones como si de variables individuales se tratase, tanto por valor como por referencia. El siguiente ejemplo busca el elemento mayor en un vector de n meros enteros. Para ello utiliza la u funci n maximo que vimos anteriormente. Obs rvese el paso por referencia del elemento i- simo de o e e un vector, a la funci n scanf y, por valor, a la funci n maximo. o o #include <stdio.h> void main() int max, v[20], i; printf( "Introducir elementos del vector:nn" ); for (i= 0; i< 20; i++)

Los autores, 2000; Edicions UPC, 2000.

97

10. Funciones

scanf( "%d", &v[i] ); max = v[0]; for (i= 1; i< 20; i++) max = maximo( max, v[i] ); printf( "El elemento mayor es:

%dnn", max );

Algo muy diferente es el paso de una tabla a una funci n. La unica manera que C ofrece para ello o es el paso por referencia de toda la tabla en cuesti n. Sin embargo, al contrario que en el paso por o referencia habitual, no se usan los smbolos & y * . En su lugar se utiliza directamente el nombre de la tabla, que constituye de por s una referencia al primer elemento de la tabla, tal como vimos en el captulo 9. Por lo tanto, el par metro formal de la funci n debe ser un puntero para poder recoger la a o direcci n de inicio de la tabla. o El siguiente ejemplo dene una funci n para el c lculo de la media de los elementos de un vector o a de n meros de coma otante: u #include <stdio.h> #define DIM 20 float media( float vec[], int n )

int j; float sum; sum = 0.0; for (j= 0; j< n; j++) sum = sum + vec[j]; return (sum/(float)n);

g f

void main() float med, v[DIM]; int i; printf( "Introducir elementos del vector:nn" ); for (i= 0; i< DIM; i++) scanf( "%f", &v[i] ); med = media( v, DIM ); printf( "La media es: %fnn", med );

La cabecera de la funci n media hubiese podido escribirse tambi n como: o e float media( float *vec, int n ) Es importante notar que la denici n del par metro vec en la funci n media es una declaraci n o a o o de un puntero. Es decir, no existe diferencia alguna entre float *vec y float vec[]. El motivo de usar esta nueva notaci n es dar mayor claridad al programa. Por lo tanto, cuando un par metro o a

Los autores, 2000; Edicions UPC, 2000.

10.5. Paso de par metros a

98

sea un puntero a una tabla usaremos la notaci n tipo nombrePuntero[], mientras que cuano do tengamos un puntero a cualquier otro tipo de datos (tanto tipos elementales como estructurados) usaremos la notaci n tipo *nombrePuntero. o Finalmente, la cabecera de la funci n media tambi n pudiera haberse escrito como: o e float media( float vec[DIM], int n ) En este caso de nuevo el par metro vec es un puntero a una tabla, pero ahora adem s indicamos el a a tama o de la tabla, lo que clarica a n m s el programa. Notar que esto s lo es posible hacerlo si las n u a o dimensi nes de la tabla son constantes. Es decir, una expresi n como o o float media( float vec[n], int n ) sera incorrecta. Vemos ahora un ejemplo con una matriz. Deseamos hacer una funci n que multiplique una matriz o por un vector. La soluci n propuesta es la siguiente: o #include <stdio.h> #define MAXFIL 3 #define MAXCOL MAXFIL void matXvec( int nfil, int ncol, float A[], float x[], float y[] ) f /*Calcula y = A*x */ int i, j; for (i= 0; i< nfil; i++)

f g

y[i] = 0.0; for (i= 0; i< ncol; i++) y[i] = y[i] + A[i*MAXCOL+j] * x[j];

g f

void main() int nfil, ncol; float v1[MAXCOL], v2[MAXFIL], M[MAXFIL][MAXCOL]; ... /* Leer los nfil, ncol, A, x */ matXvec( nfil, ncol, M, v1, v2 ); ... /* Mostrar y */

N tese que los par metros formales A, x e y son todos punteros. Se accede a los elementos de o a la matriz a trav s de un puntero que se ala al primer elemento ([0][0]). En la secci n 9.4 vimos e n o la forma de realizar un acceso de este tipo. N tese tambi n que en la f rmula que da el n mero de o e o u elementos entre el primero y el elemento [i][j], se debe usar MAXCOL y no ncol. Es decir,

Los autores, 2000; Edicions UPC, 2000.

99

10. Funciones

A A[0] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] M[0][0] ncol=2 M[0][1] M[0][2] MAXCOL = 3 M[1][0] M[1][1] M[1][2] M[2][0] M[2][1] M[2][2]
Figura 10.1: Acceso a una matriz mediante un puntero debemos contar todos los elementos que hay en memoria entre el primero y el elemento [i][j], incluyendo los que no son usados por el algoritmo. La gura 10.1 muestra este ejemplo gr camente a suponiendo que ncol vale 2, MAXCOL vale 3, y que queremos acceder el elemento [2][1] de la matriz.

i*MAXCOL+j = 2*3+1=7

10.5.4 Par metros en la funci n main a o


Hasta el momento hemos empleado la funci n main sin par metros. Sin embargo, es posible pasar o a par metros a la funci n principal del programa, desde la lnea de ordenes del sistema operativo. Los a o par metros de la funci n main son dos; se conocen tradicionalmente como argc y argv, aunque a o pueden tomar cualquier nombre. El par metro argc es un valor entero que contiene el n mero de par metros dados al programa a u a al ser ejecutado desde la lnea de ordenes del sistema operativo. El nombre del programa se considera como el primer par metro, por lo que el valor mnimo de argc es 1. a El par metro argv es un vector de punteros a cadenas de caracteres. Estas cadenas toman el valor a de cada uno de los par metros dados al programa al ejecutarlo. Cada par metro de la lnea de ordenes a a debe estar separado por un espacio o un tabulador.

Los autores, 2000; Edicions UPC, 2000.

10.6. Recursividad

100

As pues, el formato de la funci n main con par metros es el siguiente: o a

f g

void main( int argc, char *argv[] ) /* Cuerpo de la funcin. o */

El siguiente programa de ejemplo permite introducir el nombre del usuario del programa al ejecutarlo y mostrar un mensaje que lo incluya. #include <stdio.h> void main( int argc, char *argv[] ) if (argc > 2)

f g f g f g g

printf( "Demasiados parmetrosnn" ); a

else if (argc < 2) printf( "Faltan parmetrosnn" ); a

else printf( "Yo te saludo %snn", argv[1] );

As pues, si hemos llamado al programa ejecutable saludo y se escribe en la lnea de ordenes del sistema operativo saludo Pepe, la salida del programa ser : a Yo te saludo Pepe Si alguno de los par metros que se pasan al programa contiene espacios en blanco o tabuladores, a deben usarse comillas en la ejecuci n del programa. o Lnea de ordenes del sistema operativo: saludo "Pepe Prez" e Salida del programa: Yo te saludo Pepe Prez e

10.6 Recursividad
Se llama recursividad a un proceso en el que una funci n se llama a s misma repetidamente hasta o que se satisface una cierta condici n. Este proceso se emplea para c lculos repetitivos en los que el o a resultado de cada iteraci n se determina a partir del resultado de alguna iteraci n anterior. o o Frecuentemente un mismo problema puede expresarse tanto de forma recursiva como de forma iterativa. En estos casos, debido a que la ejecuci n recursiva requiere de numerosas llamadas a funciones, o es preferible utilizar una soluci n no recursiva. Sin embargo, en otros casos, la escritura de una soluo ci n no recursiva puede resultar extraordinariamente compleja. Es entonces cuando es apropiada una o soluci n recursiva. o

Los autores, 2000; Edicions UPC, 2000.

101

10. Funciones

A continuaci n se presentan dos funciones recursivas para el c lculo del factorial y el c lculo de o a a valores de la serie de Fibonacci:

f g f g

int fact( int x ) if (x <= 1) return (1); return (x * fact(x-1));

int fibo( int n ) if ((n==0) || (n==1)) return (1); return (fibo(n-1)+fibo(n-2));

Otro ejemplo de soluci n recursiva es este programa para invertir los elementos de un vector usando o la funci n swap: o #define N 10 void invert( int v[], int i, int j ) swap( &v[i], &v[j] ); i++; j--; if (i < j) invert( v, i, j );

g f

void main() int i, vector[N]; for(i= 0; i< N; i++) scanf( "%d", &vector[i] ); invert( v, 0, N-1 ); for(i= 0; i< N; i++) printf( "%dnn", vector[i] );

10.7 Ejercicios
1. El c lculo de a

ex

puede aproximarse mediante el c lculo de a

grande. Escribir una funci n pot o a o o o donde i y x son par metros enteros de la funci n. Usando esta funci n y la funci n fact del

n sucientemente e u que permita calcular la potencia i- sima de un n mero x,


=0

n X xi i i
!

para

Los autores, 2000; Edicions UPC, 2000.

10.7. Ejercicios

102

principio del captulo, escribir un programa que calcule ex de forma aproximada para un valor de n dado. 2. Escribir un programa para calcular frac del problema anterior. 3. Escribir un programa para calcular frac. 4. Se dispone de las dos funciones siguientes:

sen x
(

) =

1 X xi i i
=0

2 +1

(2

+ 1)!

1)

i . Utilizar las funciones pot y

cos x
(

) =

1 X xi i ; i
2 =0

(2 )!

1)

i . Utilizar las funciones pot y

void f1( int x, int *y, int a, int b ) x = x + *y = *y x = x + *y = *y printf( 1; + 1; a; + b; "%d %dnn", x, *y );

g f

void f2( int a, int *b ) a = a + *b = *b a = a + *b = *b printf( 1; + 1; a; + *b; "%d %dnn", a, *b );

Y del programa principal: #include <stdio.h> void main() int a = 0, b = 0; llamada printf( "%d %dnn", a, b );

Indicar el resultado de ejecutar este programa en caso de que llamada se substituya por: f1( a, &b, a, b ); o bien por f2( a, &b ); 5. A continuaci n se muestra el esqueleto de un programa en C: o

Los autores, 2000; Edicions UPC, 2000.

103

10. Funciones

int f1( char a, char b )

f g f g f

a = P; b = Q; return ((a<b)?(int)a:(int)b);

int f2( char *c1, char *c2 ) *c1 = R; *c2 = S; return ((*c1==*c2)?(int)*c1:(int)*c2);

void main() char a, b; int i, j; . . . a = X; b = Y; i = f1( a, b ); printf( "a=%c,b=%cnn", a, b ); . . . j = f2( &a, &b ); printf( "a=%c,b=%cnn", a, b );

(a) Qu valores se asignan a i y j en main? e (b) Qu valores escribe la primera llamada a printf? e (c) Qu valores escribe la segunda llamada a printf? e 6. Qu valor calcula el siguiente programa? e

void func( int p[] ) int i, sum = 0; for(i= 3; i< 7; ++i) sum = sum + p[i]; printf( "suma = %d", sum );

g f g

void main() int v[10] = f1,2,3,4,5,6,7,8,9,10g; func( &v[2] );

Los autores, 2000; Edicions UPC, 2000.

10.7. Ejercicios

104

7. Determinar si un n mero no negativo es perfecto o tiene alg n amigo. Dos n meros son amigos u u u cuando la suma de los divisores de uno de ellos es igual al otro n mero. Por ejemplo: 220 y 284 u son amigos. Por otra parte, un n mero es perfecto cuando la suma de sus divisores es el mismo. u Por ejemplo 6 = 3 + 2 + 1 es perfecto. 8. Dado el siguiente tipo de datos: #include <stdio.h> typedef struct

f g

char a[10]; char b[10]; char c[10]; Tcolores;

describir la salida generada por cada uno de los siguientes programas: (a) void func( Tcolores X )

g f

X.a = "cian"; X.b = "magenta"; X.c = "amarillo"; printf( "%s%s%snn", X.a, X.b ,X.c ); return();

void main() Tcolores col = f "rojo", "verde", "azul" g; printf( "%s%s%snn", col.a, col.b, col.c ); func( col ); printf( "%s%s%snn", col.a, col.b, col.c );

g
(b)

void func(Tcolores *X)

g f

X->a = "cian"; X->b = "magenta"; X->c = "amarillo"; printf( "%s%s%snn", X->a, X->b, X->c ); return();

void main() Tcolores col = f "rojo", "verde", "azul" g; printf( "%s%s%snn", col.a, col.b, col.c ); func( & col ); printf( "%s%s%snn", col.a, col.b, col.c );

Los autores, 2000; Edicions UPC, 2000.

105

11. Ficheros

Captulo 11

Ficheros
Es preciso alg n mecanismo que permita almacenar de forma permamente ciertos datos de los prou gramas. Por ejemplo, un programa que gestione la contabilidad de una empresa necesita una serie de informaciones iniciales (balance hasta ese momento, lista de compras, lista de ventas, etc ). De igual forma, genera una serie de informaciones que deben ser almacenadas cuando el programa naliza. Desde el punto de vista del hardware, hay diferentes dispositivos para almacenar informaci n de o forma permanente: discos duros, unidades de cinta, CDs, disquetes, etc. Para un programador, el dispositivo fsico que se use carece de importancia. Los programas deben funcionar tanto si la informaci n o est en un disco duro como en un CD como en una cinta. Por lo tanto, es preciso un conjunto de funcioa nes (una librera) que permita realizar almacenamiento permamente de informaci n, pero omitiendo los o detalles especcos de cada dispositivo hardware. Esta librera de funciones la proporciona el sistema operativo. Para ocultarle al programador los detalles especcos de cada dispositivo hardware, se usa el con cepto de chero. Un chero es un objeto abstracto sobre el cual se puede leer y escribir informaci n. o Existen dos tipos fundamentales de cheros: cheros de texto y cheros binarios. En los cheros de texto la informaci n se almacena usando caracteres (c digos ASCII). Por ejemplo, una variable de o o tipo int se almacena en la memoria como una secuencia de bits que debe ser interpretada como un c digo complemento a dos. Sin embargo, cuando escribimos dicha variable en un chero de texto, o lo que almacenamos es un conjunto de caracteres que representan el valor de la variable en base 10. Una variable tipo de int que en memoria se almacenase como 1 0 0 en un chero de texto se o escribira como ;2147483648. En los cheros binarios la informaci n se almacena de igual forma que en la memoria, mediante la misma secuencia de unos y ceros. Usar cheros de texto tiene la ventaja de que la informaci n almacenada puede ser visualizada y o comprendida por un ser humano. Pero tiene el inconveniente de ocupar aproximadamente tres veces m s espacio que los cheros binarios. Por ejemplo, la variable de tipo int mostrada anteriormente, a ocupa 4 bytes en un chero binario (lo mismo que ocupa en memoria), y ocupa 11 bytes (signo y 10 cifras) en un chero de texto. Existen diferentes funciones para trabajar con cheros de texto y con cheros binarios. En este libro nos ocuparemos unicamente de los cheros de texto. Un chero de texto almacena la informaci n como una secuencia de c digos ASCII. La gura 11.1 o o muestra un ejemplo de almacenamiento de un chero de texto. En esta gura se muestra el aspecto que

z }| {
31

Los autores, 2000; Edicions UPC, 2000.

106

Esto es un ejemplo de cmo se almacena un chero de texto.

Ventana de acceso

69 115 116 111 32 101 115 10 ... 130 116 111 EOF

Cdigo del carcter E Cdigo del carcter s Cdigo del carcter t Cdigo del carcter o Cdigo del carcter Espacio en blanco Cdigo del carcter e Cdigo del carcter s Cdigo del carcter \n Cdigo del carcter t Cdigo del carcter o Cdigo del carcter .

Figura 11.1: Almacenamiento de un chero de texto tendra un chero de texto al mostrarse en pantalla, as como la secuencia de bytes que se almacenaran en disco. Todos los cheros de texto nalizan com un car cter especial que indica el nal del chero. a Este car cter lo representamos mediante la constante EOF (End Of File) que se halla denida en el a chero stdio.h. Cuando queramos leer o escribir informaci n en un chero de texto deberemos hacerlo de forma o secuencial. Por ejemplo, si queremos leer el chero de la gura 11.1, deberemos leer en primer lugar el primer car cter y as sucesivamente. Esto se debe a que existe una ventana asociada al chero que s lo a o puede avanzar secuencialmente, nunca a saltos. El sistema operativo usa variables del tipo FILE para manejar los dispositivos hardware asociados a cheros. La denici n del tipo FILE se encuentra en el chero stdio.h. Todos los programas o que manejen cheros deber n incluir stdio.h. Una variable del tipo FILE es una estructura cuyo a contenido s lo puede ser entendido y manejado por funciones del sistema operativo. Dicha estructura o contiene informaci n, por ejemplo, de la pista y el sector del disco donde comienza el chero, etc. o Dado que las variables de tipo FILE pertenecen al sistema operativo, nunca tendremos una variable de este tipo en nuestros programas. Lo unico que necesitamos son punteros a dichas variables. Esto es, si queremos manejar un chero dentro de un programa, deberemos tener una declaraci n como la o siguiente: FILE *fp; El puntero fp debe ser inicializado mediante la funci n fopen, de forma que apunte a una o variable del sistema operativo que contenga los datos del chero concreto que usemos. Las funciones de lectura y escritura en el chero unicamente necesitan conocer dicho puntero para saber la variable que deben usar. Para utilizar un chero se debe realizar una secuencia ja de acciones:

Los autores, 2000; Edicions UPC, 2000.

107

11. Ficheros

1. Abrir el chero. Esto signica decirle al sistema operativo que inicialice una variable de tipo FILE, de forma que a partir de ese momento, las acciones de lectura/escritura que utilicen dicha variable se realicen realmente en el dispositivo hardware correspondiente. Esto se hace llamando a una funci n fopen. o 2. Leer o Escribir en el chero. Para ello usaremos las funciones fscanf y fprintf. Estas funciones s lo necesitan dos datos: la variable de tipo FILE asociada al chero y la informaci n o o que queremos leer o escribir en dicho chero. 3. Cerrar el chero. Esto signica indicar al sistema operativo que ya no necesitamos la variable tipo FILE asociada al chero. Para ello se usa la funci n fclose. o

11.1 Abrir y cerrar cheros


La cabecera de la funci n fopen es la siguiente: o FILE * fopen( char nombreFichero[], char modoAcceso[] ) donde nombreFichero es una cadena de caracteres con el nombre del chero fsico que se quiere usar, y modoAcceso es una cadena de caracteres que indica la acci n que realizaremos sobre el o chero. Existen tres modos b sicos para abrir cheros: a "r": Abrir un chero ya existente para lectura. "w": Abrir un chero nuevo para escritura. Si el chero ya exista, ser destruido y creado de a nuevo. "a": Abrir un chero ya existente para a adir informaci n; esto es, escribir al nal del mismo. n o Si el chero no exista se crear uno nuevo. a Adem s de estos modos existen otros de uso menos frecuente: a "r+": Abrir un chero ya existente tanto para lectura como escritura. "w+": Abrir un chero nuevo tanto para lectura como escritura. Si el chero ya exista ser a destruido y creado de nuevo. "a+": Abrir un chero ya existente para leer y a adir. Si el chero no exista se crear uno n a nuevo. La funci n fopen retorna la constante NULL si no ha podido abrir el chero. Esta condici n de o o error debe ser comprobada siempre que se use la funci n fopen. La constante NULL est denida o a en stdio.h. Cuando ya hemos acabado de utilizar el chero, debemos indic rselo al sistema operativo mediante a la funci n fclose, que libera la variable de tipo FILE asociada al chero. La cabecera de la funci n o o fclose es la siguiente:

Los autores, 2000; Edicions UPC, 2000.

11.1. Abrir y cerrar cheros

108

int fclose( FILE *fp ) Si no se produce ning n error al cerrar el chero fclose retorna 0. En caso contrario retorna la u constante EOF (recordemos que esta constante est denida en stdio.h). Veamos algunos ejemplos a del uso de fopen y fclose. En el siguiente ejemplo abrimos un chero llamado miFichero.txt para leer la informaci n o contenida en el. En caso de error al abrir el chero mostramos un mensaje en pantalla y nalizamos la ejecuci n del programa mediante la funci n exit. Finalmente cerramos el chero. o o #include <stdio.h> void main( ) FILE *fp; fp = fopen( "miFichero.txt", "r" ); if (fp == NULL)

f g

printf( "Error abriendo miFichero.txtnn" ); exit(0);

. . . /* Aqu podemos leer datos del fichero */ . . . fclose( fp );

A continuaci n se muestra otro ejemplo para el caso en que abrimos un chero para escritura. o El nombre del chero es introducido por el usuario a trav s del teclado. Si el chero ya existe, ser e a destruido y creado de nuevo. Esta acci n la realiza de forma autom tica la funci n fopen. Finalmente o a o cerramos el chero. #include <stdio.h> #define N 256 void main( )

FILE *fp; char nombreFichero[N]; printf( " Nombre del fichero (< %d caracteres): scanf( "%s%*c", nombreFichero ); fp = fopen( nombreFichero, "w" ); if (fp == NULL) ", N );

f g

printf( "Error abriendo %snn", nombreFichero ); exit(0); . .

Los autores, 2000; Edicions UPC, 2000.

109

11. Ficheros

/* Aqu podemos escribir datos en el fichero */ . . . fclose( fp );

11.2 Leer y escribir en cheros


Una vez abierto un chero, podemos leer y escribir en el mediante las funciones fscanf y fprint. Las cabeceras de estas funciones son: int fscanf( FILE *fp, char formato[], <lista variables> ) int fprintf( FILE *fp, char formato[], <lista variables> ) La funci n fscanf permite leer del chero apuntado por fp, mientras que la funci n fprintf pero o mite escribir en el chero apuntado por fp. El uso de estas funciones es an logo al de scanf y a printf, que permiten leer variables desde el teclado y escribir variables en pantalla, respectivamente. Por tanto, formato es una cadena de caracteres que describe el formato de las variables a leer/escribir. Por su parte, <lista variables> contiene las direcciones de memoria de todas las variables en el caso de fscanf y las variables propiamente dichas en el caso de fprintf. Los operadores de formato se hallan descritos en el ap ndice B. e La funci n fprintf retorna el n mero de bytes (caracteres) escritos en el chero, o un n mero o u u negativo en caso de que ocurra alg n error en la escritura. La funci n fscanf retorna el n mero de u o u variables correctamente ledas, o la constante EOF en caso de error. Veamos algunos ejemplo de uso de fscanf y fprintf. En el siguiente ejemplo leemos un vector de enteros de un chero de entrada. El chero contiene en la primera lnea el n mero de elementos del vector. El resto de lneas del chero contienen un elemento u del vector en cada lnea. Finalmente, el programa escribe el vector en un chero de salida usando el mismo formato que en el chero de entrada. #include <stdio.h> #define N 256 #define MAXELE 100 void main( )

FILE *fp; char nombreFichero[N]; int lon = 0; int vec[MAXELE]; printf( "Fichero de entrada(< %d caracteres): scanf( "%s%*c", nombreFichero ); fp = fopen( nombreFichero, "r" ); if (fp == NULL) ", N );

printf( "Error abriendo %snn", nombreFichero );

Los autores, 2000; Edicions UPC, 2000.

11.2. Leer y escribir en cheros

110

g f g

exit(0);

fscanf( fp, "%d", &lon ); if (lon < MAXELE) for (i= 0; i< lon; i= i+1) fscanf( fp, "%d", &vec[i] );

else printf( "El vector tiene demasiados elementosnn" ); fclose( fp ); . . . /* Aqu podemos modificar vec */ . . . printf( "Fichero de salida(< %d caracteres): ", N ); scanf( "%s%*c", nombreFichero ); fp = fopen( nombreFichero, "w" ); if (fp == NULL)

f g

printf( "Error abriendo %snn", nombreFichero ); exit(0);

fprintf( fp, "%dnn", lon ); for (i= 0; i< lon; i= i+1) fprintf( fp, "%dnn", vec[i] ); fclose( fp );

En el ejemplo anterior, si el nombre del chero de salida es el mismo que el nombre del chero de entrada, los datos iniciales se perder n, ya que al abrir el chero en modo "w", el chero que ya exista a es destruido y creado de nuevo. En este ejemplo, sin embargo, leemos de un chero y el resultado del programa es a adido al nal del mismo chero. n #include <stdio.h> #define N 256 #define MAXELE 100 void main( )

FILE *fp; char nombreFichero[N]; int lon; int vec[MAXELE]; printf( "Nombre del fichero(< %d caracteres): scanf( "%s%*c", nombreFichero ); fp = fopen( nombreFichero, "r" ); if (fp == NULL) ", N );

Los autores, 2000; Edicions UPC, 2000.

111

11. Ficheros

f g f g

printf( "Error abriendo %snn", nombreFichero ); exit(0);

fscanf( fp, "%d", &lon ); if (lon < MAXELE) for (i= 0; i< lon; i= i+1) fscanf( fp, "%d", &vec[i] );

else printf( "El vector tiene demasiados elementosnn" ); fclose( fp ); . . . /* Aqu trabajamos con vec */ . . . fp = fopen( nombreFichero, "a" ); if (fp == NULL)

f g

printf( "Error abriendo %snn", nombreFichero ); exit(0);

fprintf( fp, "%dnn", lon ); for (i= 0; i< lon; i= i+1) fprintf( fp, "%dnn", vec[i] ); fclose( fp );

11.3 Otras funciones para el manejo de cheros


11.3.1 feof
En la mayora de ocasiones debemos leer un chero sin saber su tama o a priori y, por lo tanto, sin n saber la cantidad de datos que debemos leer. En esta situaci n se hace necesaria una funci n que nos o o indique cu ndo se alcanza el nal de chero. Esta funci n es feof, cuya cabecera es la siguiente: a o int feof( FILE *fp ) La funci n feof retorna un n mero diferente de 0 (cierto) cuando el car cter especial EOF ha o u a sido alcanzado en una lectura previa del chero se alado por fp. En caso contrario, retorna 0 (falso). n Es muy importante notar que la funci n feof s lo indica n de chero si previamente hemos o o realizado una lectura mediante fscanf que no ha podido leer nada (que ha alcanzado el car cter a EOF). Veamos algunos ejemplos del uso de feof. El programa del siguiente ejemplo lee de un chero los elementos de un vector de enteros. El chero contiene un elemento del vector en cada lnea. N tese que en el bucle while se controlan dos o condiciones: alcanzar el n del chero y llenar completamente el vector.

Los autores, 2000; Edicions UPC, 2000.

11.3. Otras funciones para el manejo de cheros

112

#include <stdio.h> #define N 256 #define MAXELE 100 void main( )

FILE *fp; char nombreFichero[N]; int lon; int vec[MAXELE]; printf( " Nombre del fichero(< %d caracteres): scanf( "%s%*c", nombreFichero ); fp = fopen( nombreFichero, "r" ); if (fp == NULL) ", N );

f g f

printf( "Error abriendo %snn", nombreFichero ); exit(0);

lon = 0; while (!feof(fp) && (lon < MAXELE)) kk = fscanf( fp, if (kk == 1) lon++; if (!feof(fp) && printf( "Todo cabe "%d", &vec[lon] );

g g

(lon == MAXELE)) el contenido del fichero no en el vectornn" );

fclose( fp ); . . .

Supongamos que el chero contiene s lo tres lneas como las siguientes: o 123 254 -35 Al ejecutar el programa, el bucle while realizar cuatro iteraciones. En la tercera iteraci n se leer a o a del chero el n mero -35 y se almacenar en vec[2]. Sin embargo, la funci n feof a n no u a o u indicar el nal del chero, es decir, retornar 0. En la cuarta iteraci n, la funci n fscanf detectar a a o o a el car cter EOF y por lo tanto no podr leer ning n valor v lido. As pues, en vec[3] no almaa a u a cenamos nada (se queda como estaba, con un valor aleatorio). Podremos saber que esta situaci n ha o ocurrido consultando el valor retornado por la funci n fscanf. En este ejemplo, como s lo leemos o o una variable, fscanf debe retornar 1 si ha podido realizar una lectura correcta. N tese que el proo grama s lo incrementa el valor de lon si la lectura ha sido correcta. Despu s de la cuarta iteraci n, o e o feof retornar un valor diferente de 0 (cierto). a

Los autores, 2000; Edicions UPC, 2000.

113

11. Ficheros

11.3.2 ferror
La cabecera de esta funci n es la siguiente: o int ferror( FILE *fp ) La funci n ferror retorna un valor diferente de 0 si ha ocurrido alg n error en una lectuo u ra/escritura previa en el chero se alado por fp. En caso contrario retorna 0. n

11.3.3 fflush
La cabecera de esta funci n es la siguiente: o int fflush( FILE *fp ) Cuando escribimos en un chero, en realidad la escritura no se produce en el mismo momento de ejecutar la funci n fprintf. Sin embargo, esta funci n deja la informaci n a escribir en un o o o buffer temporal del sistema operativo. M s tarde, cuando el sistema operativo lo decida (est libre a e de otras tareas, por ejemplo), se vuelca el contenido de dicho buffer sobre el chero fsico. De esta forma en un computador con varios usuarios se puede organizar de forma m s eciente el acceso a las a unidades de almacenamiento de informaci n. La funci n fflush puede utilizarse para forzar en o o el instante deseado el volcado del buffer sobre el chero. Si no se produce ning n error, la funci n u o fflush retorna 0, en caso contrario retorna EOF. Un ejemplo tpico del uso de fflush es la depuraci n de programas. Supongamos que tenemos o un error en un programa y para encontrarlo ponemos diversos fprintf, que muestran valores de algunas variables. Si no ponemos despu s de cada fprintf una llamada a fflush, no veremos el e valor que queremos en el momento en que realmente se produce, lo que nos puede llevar a conclusiones err neas sobre el comportamiento del programa. o

11.4 Ficheros est ndar: stdin, stdout, stderr a


En C existen tres constantes del tipo FILE *, denidas en stdio.h, llamadas stdin, stdout y stderr. El puntero stdin apuntan a un chero abierto s lo para lectura. Los punteros stdout y o stderr apuntan a cheros abiertos s lo para escritura. o Estos punteros est n inicializados por el sistema operativo de forma que una lectura de stdin sea a en realidad una lectura del teclado. Es decir, que una llamada como fscanf( stdin, "%d", &i ) es equivalente a scanf( "%d", &i ) . De igual forma, los cheros asignados a stdout y stderr est n inicialmente redirigidos a la a pantalla, de forma que fprintf( stdout, "Holann" ) o fprintf( stderr, "Holann" ) tienen el mismo efecto que printf( "Holann" ) . Las constantes stdin, stdout y stderr pueden ser usadas para inicializar varibles del tipo FILE * de forma que la entrada/salida sea a trav s de teclado/pantalla. e

Los autores, 2000; Edicions UPC, 2000.

11.4. Ficheros est ndar: stdin, stdout, stderr a

114

El siguiente ejemplo muestra un programa para multiplicar una matriz por un vector. Los datos de entrada se leen de stdin, es decir, del teclado. Por otra parte, los datos de salida se escriben en stdout (pantalla), y los mensajes de error en stderr (tambi n la pantalla). N tese que stdin, e o stdout y stderr no est n declaradas en el programa, puesto que ya lo est n en stdio.h. Cuando a a el programa funciona usando teclado/pantalla, muestra una serie de mensajes en pantalla explicando al usuario los datos que debe introducir. Sin embargo, cuando se usan cheros, estos mensajes no tienen sentido, por lo que no son mostrados. #include <stdio.h> #define MAXFILAS 10 #define MAXCOLUMNAS MAXFILAS void main( )

int i, j, k, Nfilas, Ncolumnas; double x[MAXCOLUMNAS], y[MAXFILAS]; double A[MAXFILAS][MAXCOLUMNAS]; char car; FILE *fi = stdin; FILE *fo = stdout; FILE *fe = stderr; printf( "Entrada/Salida por ficheros? scanf( "%c", &car ); if (car == s|| car == S) (s/n)" );

fi = fopen( "Entrada.txt", "r" ); if (fi == NULL)

f g f g f g g

printf( "Error abriendo Entrada.txtnn" ); exit(0);

fo = fopen( "Salida.txt", "w" ); if (fo == NULL) printf( "Error abriendo Salida.txtnn" ); exit(0);

fe = fopen( "Errores.txt", "w" ); if (fe == NULL) printf( "Error abriendo Errores.txtnn" ); exit(0);

if (fo == stdout) fprintf( fo, " N. filas = " );

Los autores, 2000; Edicions UPC, 2000.

115

11. Ficheros

fscanf( fi, "%d", &Nfilas ); if (Nfilas > MAXFILAS)

f g

fprintf( fe, "Demasiadas filasnn" ); exit (0);

if (fo == stdout) fprintf( fo, " N. columnas = " ); fscanf( fi, "%d", &Ncolumnas ); if (Ncolumnas > MAXCOLUMNAS)

f g

fprintf( fe, "Demasiadas columnasnn" ); exit (0);

for (i= 0; i< Nfilas; i++) for (j= 0; j< Ncolumnas; j++)

if (fo == stdout) fprintf( fo, "A[%d][%d] = ", i, j ); k = fscanf( fi, "%lf", &A[i][j] ); if (k != 1)

f g f g

fprintf( fe, "Error leyendo la matriznn" ); exit (0);

for (i= 0; i< Nfilas; i++) if (fo == stdout) fprintf( fo, "x[%d] = ", i ); k = fscanf( fi, "%lf", &x[i] ); if (k != 1)

f g f g

fprintf( fe, "Error leyendo el vectornn" ); exit (0);

for (i= 0; i< Nfilas; i++) y[i] = 0.0; for (j= 0; j< Ncolumnas; j++) y[i] = y[i] + A[i][j] * x[j];

Los autores, 2000; Edicions UPC, 2000.

11.5. Ejercicios

116

g
for (i= 0; i< Nfilas; i++) fprintf( fo, "y[%d] = %lfnn", i, y[i] ); if (fi != stdin) fclose( fi ); if (fo != stdout) fclose( fo ); if (fe != stderr) fclose( fe );

11.5 Ejercicios
1. Se dispone de dos cheros con n meros enteros ordenados de menor a mayor. Escribir los siu guientes programas de forma que el chero resultante contenga los n meros ordenados de mayor u a menor. Un programa que construya un chero con todos los n meros que est n en ambos cheros u a simult neamente (AND de cheros). a Un programa que construya un chero con todos los n meros que est n en cualquiera de u a los dos cheros (OR de cheros). En el chero resultante no debe haber n meros repetidos. u Un programa que construya un chero con los n meros que est n en cualquiera de los dos u a cheros, pero no en los dos simult neamente (XOR de cheros). a 2. Se dispone de un chero que contiene texto y se pretende realizar una compactaci n del mismo. o Para ello se substituyen las secuencias de cuatro o m s caracteres blancos por la secuencia #n# a , donde n indica el n mero de caracteres blancos que se han substituido. Para evitar confuci n, u o el car cter # se sustituye por ##. Dise ar una funci n que lea de un chero un texto no a n o compactado y lo compacte seg n los criterios expuestos. Dise ar tambi n otra funci n que lea u n e o un chero de texto resultado de una compactaci n previa y lo descompacte. o 3. Se dispone de un chero que contiene un n mero no determinado de etiquetas. Cada etiqueta u es un conjunto de datos sobre un determinado individuo (nombre, direcci n, tel fono, etc). La o e etiqueta est formada por 3 lneas consecutivas de texto. Cada lnea de texto tiene 15 caracteres a como m ximo. Las etiquetas est n separadas por una lnea que contiene unicamente el car cter *. a a a Se desea dise ar un programa que permita construir un nuevo chero que contenga las etiquetas n del chero original pero organizadas en columnas de 3 etiquetas (que empezar n respectivamente a en las columnas 0, 20 y 40). Supondremos que las lneas de un chero pueden tener un m ximo de a 80 caracteres. Las las de etiquetas deben estar separadas por una lnea en blanco. Por ejemplo: Fichero de Entrada Juan Prez e c/ Aragn o Tlf. 932 491 134

Los autores, 2000; Edicions UPC, 2000.

117

11. Ficheros

Pedro Lpez o Avd. Europa Tlf. 931 113 456 Juan Garca c/ Gracia Lrida e Andrs Villa e Tlf. 931 113 457 Badalona Pedro Cubero Tlf. 971 456 789 Mallorca

Fichero de Salida Juan Prez e c/ Aragn o Tlf. 932 491 134 Andrs Villa e Tlf. 931 113 457 Badalona

Pedro Lpez o Avd. Europa Tlf. 931 113 456 Pedro Cubero Tlf. 971 456 789 Mallorca

Juan Garca c/ Gracia Lrida e

4. Se dispone de un chero compuesto unicamente por letras may sculas, espacios en blanco, comas u y puntos. El contenido de este chero tiene las siguientes caractersticas: Entre palabra y palabra puede haber cualquier n mero de espacios en blanco. u Entre una palabra y un signo de puntuaci n puede haber cualquier n mero de espacios en o u blanco. Entre un signo de puntuaci n y una palabra puede haber cualquier n mero de espacios en o u blanco. El primer y ultimo car cter del texto de entrada es una letra. a Debemos realizar un algoritmo que escriba en un chero de caracteres el contenido del chero de entrada formateado de tal manera que en el texto resultante se cumplan los siguientes requisitos: Todas las palabras estar n escritas con letras min sculas excepto la primera letra despu s a u e de un punto y la primera letra del texto. Entre palabra y palabra s lo puede haber un blanco. o Entre la ultima letra de una palabra y un signo de puntuaci n no debe haber ning n blanco. o u

Los autores, 2000; Edicions UPC, 2000.

11.5. Ejercicios

118

Entre un signo de puntuaci n y la primera letra de una palabra debe haber un espacio en o blanco. El ultimo car cter debe ser un punto. a

Los autores, 2000; Edicions UPC, 2000.

119

A. El preprocesador

Ap ndice A e

El preprocesador
El preprocesador es una herramienta muy util para el programador. Las directivas del preprocesador son en realidad simples comandos de edici n, es decir, comandos que modican el chero con c digo o o fuente del programa, de igual forma que lo haramos mediante un editor de textos. El chero modicado por el preprocesador sigue siendo un chero de texto. Las directivas del preprocesador se distinguen de las lneas de c digo C porque empiezan con el o smbolo # en la primera columna. Es importante hacer notar que es obligatorio que el smbolo # est e en la primera columna, ya que en caso contrario se gener un error de compilaci n. a o En este ap ndice veremos las directivas m s importantes del preprocesador. e a

A.1 Directiva include


La directiva include permite incluir en el chero de c digo fuente otros cheros de texto. Esta o directiva puede usarse dos formas distintas: #include <fichero.h> #include "miFichero.h" Cuando el chero incluido pertenece al sistema operativo se usan los smbolos < > para delimitar el nombre del chero. Si el chero no forma parte del sistema operativo se usan los smbolos " ". En cualquier caso, el efecto de un include es el mismo: se sustituye la lnea donde aparece la directiva por el contenido del chero indicado.

A.2 Directivas define y undef


Como su nombre indica, la directiva define permite denir smbolos. Por su parte, la directiva undef permite eliminar smbolos previamente denidos. El uso de estas directivas es el siguente: #define nombreSmbolo valorSmbolo #undef nombreSmbolo donde nombreSmbolo es el nombre del smbolo que denimos/eliminamos y valorSmbo lo es el valor que damos a dicho smbolo. Dar valor al smbolo es optativo.

Los autores, 2000; Edicions UPC, 2000.

A.3. Directivas ifdef y ifndef

120

El principal uso de la directiva define es substituir un texto por otro texto. Por ejemplo: #define N 100 signica que el preprocesador sustituir el smbolo N por el texto 100 dentro del programa. A a continuaci n se muestra un fragmento de c digo antes y despu s de ser tratado por el preprocesador: o o e Antes del preprocesador ... for (i= 0; i< N; i++) Numeros[i] = i; ... Despu s del preprocesador e ... for (i= 0; i< 100; i++) Numeros[i] = i; ... N tese que la palabra Numeros no ha sido substituida por 100umeros. S lo se ha substituido el o o texto N all donde las reglas sint cticas de C indican que dicho texto es el nombre de un smbolo. a Esto se aprovecha para la denici n de constantes. Normalmente estas constantes son las dimeno siones m ximas de tablas del programa. De esta forma, cuando deseemos modicar estas dimensiones, a bastar modicar la lnea de c digo que contiene el define, sin tener que buscar por el programa a o todas las apariciones de las constantes. La directiva define tiene otros usos importantes que veremos en las secciones A.3 y A.4.

A.3 Directivas ifdef y ifndef


En ocasiones es preciso que determinados fragmentos de c digo fuente s lo se compilen si se cumplen o o ciertas condiciones. A este hecho se le denomina compilaci n condicional. Las directivas ifdef y o ifndef sirven para realizar dicho tipo de compilaci n. El uso de estas directivas es el siguiente: o #ifdef nombre cdigo1 o #else cdigo2 o #endif #ifndef nombre cdigo1 o #else cdigo2 o #endif donde nombre es un smbolo denido mediante la directiva define. Los textos indicados por cdigo1 y cdigo2 representan fragmentos de c digo fuente en C. En la directiva ifdef, si o o o

Los autores, 2000; Edicions UPC, 2000.

121

A. El preprocesador

existe un define que dena el smbolo nombre el c digo que nalmente se complia corresponde al o fragmento indicado por cdigo1. En caso contrario, el c digo compilado corresponde a cdigo2. o o o Por otra parte, en la directiva ifndef, si no existe un define para el smbolo nombre, el c digo compilado es el correspondiente a cdigo1. En caso contrario, el c digo compilado es el o o o correspondiente a cdigo2. En ambas directivas el uso de else es optativo. Veamos algunos o ejemplos. Supongamos que un programa debe mostrar ciertos valores en pantalla para estar seguros de su funcionamiento. Pero esto s lo es necesario hacerlo mientras el programa est en la fase de desarrollo. o a Una vez nalizada esta fase, no es necesario que muestre toda esa informaci n. Una soluci n consistir o o a en borrar manualmente las lneas de c digo pertinentes, pero si el programa es grande (miles o millones o de lneas de c digo) podemos cometer errores f cilmente al eliminar dichas lneas. En el siguiente o a c digo, el smbolo DEBUG controla si se compila o no el printf. N tese que la directiva #define o o DEBUG no le asigna valor a la constante DEBUG, smplemente la dene como smbolo. #define DEBUG ... for (i= 0; i< Nfilas; i++) y[i] = 0.0; for (j= 0; j< Ncolumnas; j++)

#ifdef DEBUG printf( "y[%d]= %lf, x[%d]= %lf, A[%d][%d]= %lfnn", i, y[i], j, x[j], i, j, A[i][j] ); #endif y[i] = y[i] + A[i][j] * x[j];

Supongamos ahora que necesitamos mostrar en pantalla los recursos que usa un programa (memoria, tiempo de ejecuci n, etc). Para ello debemos llamar a una funci n del sistema operativo. Pero en o o cada sistema operativo el nombre de dicha funci n puede ser diferente, o puede que incluso no exista o dicha funci n. El siguiente c digo muestra una soluci n para que, en cualquier caso, el programa se o o o pueda compilar sin problemas: ... printf( "Recursos usados por el programann" ); #ifdef WINDOWS printf( "Funcion no disponible en sistema WINDOWSnn" ); #else getrusage( RUSAGE SELF, &rusage ); ... #endif ...

Los autores, 2000; Edicions UPC, 2000.

A.4. Macros

122

A.4 Macros
La directiva define tambi n permite denir macros. La sintaxis de una macro es la siguiente: e #define nombreMacro( param1, param2, ... ) cdigo o

donde cdigo es un conjunto v lido de sentencias en C, y param1, etc. son smbolos que aparecen o a en cdigo. Cuando el preprocesador se ejecuta, substituye cada llamada a la macro por el texto o escrito en cdigo, substituyendo dentro de cdigo los smbolos param1, etc. por los valores que o o tengan en la llamada a la macro. Veamos un ejemplo: #define SWAP( p1, p2, p3 ) p3=p1; p1=p2; p2=p3;

En el c digo anterior hemos denido la macro SWAP. Esta macro tiene tres par metros p1, p2 y o a p3. Donde esta macro sea invocada, el preprocesador susbtituir la macro por el c digo indicado. Esta a o macro sirve para intercambiar los valores de los par metros p1 y p2, usando el par metro p3 como a a una variable temporal. Veamos a continuaci n qu hara el preprocesador con dos llamas a la macro o e SWAP en el siguente programa: Antes del preprocesador double x, y, z; int a, b, c; ... SWAP ( x, y, z ); SWAP ( a, b, c ); ... Despu s del preprocesador e double x, y, z; int a, b, c; ... z=x; x=y; y=z; c=a; a=b; b=c; ... Una macro siempre se puede substituir por una funci n. Pero si las sentencias de la macro son muy o simples, podemos gastar m s tiempo llamando y retornando de la funci n que ejecutando su c digo. a o o Adem s, en un ejemplo como el anterior vemos que la misma macro sirve para valores enteros, reales, a caracteres, estructuras, etc. Sin embargo necesitaramos una funci n diferente para cada tipo de datos o distinto.

Los autores, 2000; Edicions UPC, 2000.

123

B. La librera est ndar a

Ap ndice B e

La librera est ndar a


Como se ha visto, una de las caractersticas de C es que su sintaxis se basa en un conjunto muy reducido de palabras reservadas (ver Tab. 3.1). Por esta raz n, las operaciones de entrada y salida, el manejo de o cadenas de caracteres, las funciones matem ticas, etc. no forman parte propiamente del lenguaje C. a Todas estas funcionalidades, y muchas otras m s, se hallan implementadas en una librera de funciones a y tipos de datos especiales que se conoce como la librera est ndar. a El programador puede acceder a la librera est ndar mediante un conjunto de cheros de cabeceras a (con extensi n .h). As pues, si un programa utiliza alguna de las funciones de la librera, dicho o programa deber incluir el chero de cabeceras correspondiente donde se halle denida dicha funci n. a o Por ejemplo, si el programa utiliza una funci n de entrada y salida como printf, deber incluir el o a chero stdio.h de la siguiente forma: #include <stdio.h>. Este ap ndice resume algunas de las funciones disponibles en dicha librera agrup ndolas seg n e a u el chero de cabeceras donde se hallan denidas. Para cada funci n, se proporciona su nombre, o par metros y resultado devuelto, as como una breve descripci n. Cabe decir que alguno estos datos a o puede diferir de un sistema a otro, por lo que es recomendable consultar los manuales correspondientes para mayor seguridad.

B.1 Manipulaci n de cadenas de caracteres o


Las siguientes funciones se hallan denidas en el chero de cabeceras string.h: int strcasecmp( char s1[], char s2[] ) Compara las dos cadenas s1 y s2, ignorando may sculas y min sculas. Devuelve un entero menor, igual o mayor que 0, si s1 es u u menor, igual o mayor lexicogr camente que s2, respectivamente. a char *strcat( char dest[], char src[] ) Concatena la cadena src al nal de la cadena dest. La cadena dest debe tener suciente espacio para albergar la cadena resultante. char *strchr( char s[], int c ) Devuelve un puntero a la posici n de la primera o ocurrencia del car cter c en la cadena s. a int strcmp( char s1[], char s2[] ) Compara las dos cadenas s1 y s2. Dea vuelve un entero menor, igual o mayor que 0, si s1 es menor, igual o mayor lexicogr camente

Los autores, 2000; Edicions UPC, 2000.

B.2. Entrada y salida

124

que s2, respectivamente. char *strcpy( char dest[], char src[] ) Copia la cadena scr en la cadena dest. La cadena dest debe tener suciente espacio para albergar la cadena src. char *strdup( char s[] ) Devuelve un puntero a una nueva cadena que es un duplicado de la cadena s. int strlen( char s[] ) Devuelve la longitud de la cadena s, sin contar el car cter a n0. char *strncat( char dest[], char src[], int n ) Similar a strcat, a excepci n de que s lo se concatenan al nal de dest, los n primeros caracteres de la cadena o o src. int strncmp( char s1[], char s2[], int n ) Similar a strcmp, a excepci n o de que s lo se comparan los n primeros caracteres de ambas cadenas. o char *strncpy( char dest[], char src[], int n ) Similar a strcpy, a excepci n de que s lo se copian en dest los n primeros caracteres de la cadena src. o o int strncasecmp( char s1[], char s2[], int n ) Similar a strcasecmp, a excepci n de que s lo se comparan los n primeros caracteres de ambas cadenas. o o char *strrchr( char s[], int c ) Devuelve un puntero a la posici n de la ultima o ocurrencia del car cter c en la cadena s. a char *strstr( char s1[], char s2[] ) Devuelve un puntero a la posici n de la o primera ocurrencia de la cadena s2 en la cadena s1.

B.2 Entrada y salida


Las siguientes funciones se hallan denidas en el chero de cabeceras stdio.h.

B.2.1 Entrada y salida b sica a


Hay varias funciones que proporcionan entrada y salida b sica. Probablemente las m s conocidas sean: a a int getchar() Lee un car cter del teclado. a char *gets( char string[] ) Lee una cadena de caracteres del teclado. int putchar( char ch ) Escribe un car cter por pantalla. Devuelve el car cter escrito. a a int puts( char string[] ) Escribe una cadena de caracteres por pantalla. Devuelve el n mero de caracteres escritos. u

B.2.2 Entrada y salida con formato


Ya hemos visto el uso de la entrada y salida con formato mediante las funciones printf y scanf. Ve moslas ahora con m s detalle: a a

Los autores, 2000; Edicions UPC, 2000.

125

B. La librera est ndar a

int printf( char format[], ...

Escribe en pantalla la lista de argumentos de acuerdo con el formato especicado para cada uno de ellos, y devuelve el n mero de caracteres escritos. El formato consta de caracteres ordinarios (que se escriben u directamente en pantalla) y de especicadores de formato denotados por el car cter %. Debe haber a tantos especicadores de formato como argumentos. La forma general de uno de estos especicadores es la siguiente: %[-|+][ancho][.prec][h|l|L]tipo donde: tipo especica el tipo de datos del argumento seg n la tabla: u tipo c i, d o x, X u s f e, E g, G p % Argumento char int char * double/oat puntero ninguno Formato de salida car cter a entero decimal: dgitos 0, . . . , 9 entero octal: dgitos 0, . . . , 7 entero hexadecimal: dgitos 0, . . . , 9, A, . . . , F entero decimal sin signo cadena de caracteres hasta n0 [-]dddd.dddd notaci n cientca: [-]d.dddd[e/E][+/-]ddd o la forma m s compacta entre %f y %e a direcci n de memoria o car cter % a

[h|l|L] como modicador del tipo de datos b sico. Se usa h para short int, l para a long int y double, y L para long double. [.prec] n mero de decimales al escribir un n mero de coma otante, o n mero de caracteres u u u al escribir una cadena de caracteres. [ancho] n mero de espacios empleados para la escritura. Si es inferior al necesario se ignora. u ancho puede tomar dos valores: n Se emplean n espacios rellenando con blancos el espacio sobrante. 0n Se emplean n espacios rellenando con 0s el espacio sobrante. [-|+] Se usa - para justicar a la izquierda rellenando con blancos, y + para forzar la escritura del signo en los n meros. u Veamos algunos ejemplos ilustrativos: printf( printf( printf( printf( printf( "%030.5f", 1.5236558 ); "%+30.5f", 1.5236558 ); "%+-030.5f", 1.5236558 ); "%8.3s", "hola" ); "%-08.3s", "hola" ); 000000000000000000000001.52326 +1.52326 +1.523260000000000000000000000 hol hol00000

Los autores, 2000; Edicions UPC, 2000.

B.2. Entrada y salida

126

int scanf( char format[], ...

Lee datos del teclado (car cter a car cter) y los coloca en las direcciones de memoria especicadas en la a a lista de argumentos de acuerdo con el formato. Devuelve el n mero de argumentos ledos. El formato u consta de caracteres ordinarios (que se espera se tecleen) y de especicadores de formato denotados por el car cter %. Debe haber tantos especicadores de formato como direcciones de argumentos donde a almacenar los datos ledos. La forma general de uno de estos especicadores es la siguiente: %[*][ancho][h|l|L]tipo donde: tipo especica el tipo de datos del argumento seg n la tabla: u tipo c i d o x u I D O X U s f e, E g, G p % Argumento char * int * long int * char [] double/oat puntero ninguno Entrada esperada car cter a entero decimal, octal o hexadecimal entero decimal entero octal entero hexadecimal entero decimal sin signo entero decimal, octal o hexadecimal entero decimal entero octal entero hexadecimal entero decimal sin signo cadena de caracteres hasta blanco, tabulador o salto de lnea n mero en coma otante u direcci n de memoria hexadecimal: YYYY:ZZZZ o ZZZZ o car cter % a

* no asigna el argumento ledo a ninguna variable de los argumentos. El resto de campos del modicador son id nticos al caso de printf. Sin embargo existen un par e de convenciones especiales que merece la pena destacar: %[set] que permite leer una cadena de caracteres hasta encontrar un car cter que no pertenezca a al conjunto set especicado. Dicho car cter no se lee. a %[set] que permite leer una cadena de caracteres hasta encontrar un car cter que pertenezca a al conjunto set especicado. Dicho car cter no se lee. a

B.2.3 Ficheros
int fclose( FILE *fich ) Cierra el chero fich y devuelve un c digo de error. o int feof( FILE *fich ) Comprueba si se ha llegado al nal del chero fich.

Los autores, 2000; Edicions UPC, 2000.

127

B. La librera est ndar a

int ferror( FILE *fich ) Comprueba si se ha producido alg n error en alguna operau ci n sobre el chero fich. o int fflush( FILE *fich ) Fuerza la escritura en disco de las escrituras diferidas realizadas sobre fich. int fgetc( FILE *fich ) Lee un car cter de fich. a char *fgets( char string[], int max, FILE *fich ) Lee una cadena de hasta max caracteres de fich. FILE *fopen( char nombre[], char modo[] ) Abre el chero con el nombre y modo de apertura especicados. modo puede ser: "r" para lectura, "w" para escritura y "a" para a adir informaci n al nal del chero. n o int fprintf( FILE *fich, char formato[], ... fich. Ver printf. ) Escritura con formato en

int fputc( int c, FILE *fich ) Escribe el car cter c en fich. a int fputs( char string[], FILE *fich ) Escribe una cadena de caracteres en fich. int fscanf( FILE *fich, char formato[], ... fich. Ver scanf. int getc( FILE *fich ) Lee un car cter de fich. a int putc( int c, FILE *fich ) Escribe el car cter c en fich. a void rewind( FILE *fich ) Sit a el cursor para lecturas/escrituras de fich al prinu cipio del mismo. int sprintf( char string[], char formato[], ... mato en una cadena caracteres. Ver printf. int sscanf( char buffer[], char formato[], ... de una cadena de caracteres. Ver scanf. ) Escritura con for) Lectura con formato de

) Lectura con formato

int ungetc( int c, FILE *fich ) Devuelve el car cter c, ledo previamente, al a chero fich de donde fue ledo.

B.3 Funciones matem ticas a


Las siguientes funciones se hallan denidas en el chero de cabeceras math.h: double acos( double x ) Calcula el arco coseno de x. double asin( double x ) Calcula el arco seno de x. double atan( double x ) Calcula el arco tangente de x.

Los autores, 2000; Edicions UPC, 2000.

B.4. Clasicaci n y manipulaci n de caracteres o o

128

double atan2( double y, double x ) Calcula el arco tangente de y/x. double ceil( double x ) Calcula el entero m s peque o que es mayor que x. a n double cos( double x ) Calcula el coseno de x en radianes. double cosh( double x ) Calcula el coseno hiperb lico de x. o double exp( double x ) Calcula ex . double fabs( double x ) Calcula el valor absoluto de x. double floor( double x ) Calcula el entero m s grande que es menor que x. a labs( long n ) Calcula el valor absoluto de n. double log( double x ) Calcula el logaritmo natural de x. double log10( double x ) Calcula el logaritmo en base 10 de x. double pow( double x, double y ) Calcula xy . double sin( double x ) Calcula el seno de x en radianes. double sinh( double x ) Calcula el seno hiperb lico de x. o double sqrt( double x ) Calcula la raz cuadrada de x. void srand( unsigned seed ) Fija un nuevo germen para el generador de n meros u aleatorios (rand). double tan( double x ) Calcula la tangente de x en radianes. double tanh( double x ) Calcula la tangente hiperb lica de x. o

B.4 Clasicaci n y manipulaci n de caracteres o o


Las siguientes funciones se hallan denidas en el chero de cabeceras ctype.h: int isalnum( int c ) Devuelve cierto si c es un car cter alfanum rico. a e int isalpha( int c ) Devuelve cierto si c es una letra. int isascii( int c ) Devuelve cierto si c corresponde a un c digo ASCII. o int iscntrl( int c ) Devuelve cierto si c es un car cter de control. a int isdigit( int c ) Devuelve cierto si c es un dgito decimal. int isgraph( int c ) Devuelve cierto si c es un car cter gr co. a a int islower( int c ) Devuelve cierto si c es una letra min scula. u int isprint( int c ) Devuelve cierto si c es un car cter imprimible. a

Los autores, 2000; Edicions UPC, 2000.

129

B. La librera est ndar a

int ispunct( int c ) Devuelve cierto si c es un smbolo de puntuaci n. o int isspace( int c ) Devuelve cierto si c es un car cter de espaciado. a int isupper( int c ) Devuelve cierto si c es una letra may scula. u int isxdigit( int c ) Devuelve cierto si c es un dgito hexadecimal. int toascii( int c ) Obtiene el c digo ASCII de c. o tolower( int c ) Convierte c a min scula. u int toupper( int c ) Convierte c a may scula. u

B.5 Conversi n de datos o


Las siguientes funciones se hallan denidas en el chero de cabeceras stdlib.h: double atof( char string[] ) Convierte una cadena de caracteres en un n mero de u coma otante. int atoi( char string[] ) Convierte una cadena de caracteres en un n mero entero. u int atol( char string[] ) Convierte una cadena de caracteres en un n mero entero u de doble precisi n. o

B.6 Manipulaci n de directorios o


Las siguientes funciones se hallan denidas en el chero de cabeceras dir.h: int chdir( char path[] ) Cambia el directorio de trabajo actual de acuerdo con el path especicado. char *getcwd( char path[], int numchars ) Devuelve el nombre del directorio de trabajo actual. int mkdir( char path[] ) Crea un nuevo directorio con el nombre especicado en path. int rmdir( char path[] ) Borra el directorio con el nombre especicado en path.

B.7 Memoria din mica a


Una de las caractersticas m s importantes de un programa es la cantidad de memoria que necesita para a ejecutarse. Es importante que un programa no desperdicie memoria. Esto plantea un serio problema cuando declaramos las variables, esencialmente las tablas, ya que deberemos dimensionar el espacio de memoria para el peor caso posible. Para evitar este problema existen funciones que permiten una gesti n din mica de la memoria, es o a decir, permiten que un programa adquiera memoria seg n la necesite, y la vaya liber ndola cuando deje u a

Los autores, 2000; Edicions UPC, 2000.

B.7. Memoria din mica a

130

de necesitarla. C dispone de las siguiente funciones para gestionar de forma din mica la memoria, todas a ellas est n denidas en el chero stdlib.h: a void *malloc( size t num bytes ) Reserva un bloque de memoria de num bytes bytes. Devuelve un puntero al primer byte del bloque de memoria reservado, o NULL si no hay suciente memoria disponible. void *calloc( size t num elems, size t tam elem ) Reserva un bloque de memoria capaz de almacenar num elems de tam elem bytes cada uno. Este espacio de memoria es inicializado con ceros. Devuelve un puntero al primer byte del bloque de memoria reservado, o NULL si no hay suciente memoria disponible. void *realloc( void *ptr, size t num bytes ) Cambia el tama o del bloque de memoria apuntado por ptr para que tenga num bytes bytes. n Devuelve un puntero al primer byte del nuevo bloque de memoria reservado, o NULL si no hay suciente memoria disponible. void free( void *ptr ) Libera el bloque de memoria apuntado por ptr. Dicho bloque debe haber sido previamente obtenido mediante malloc, calloc o realloc. Si ptr es NULL, no se hace nada. u El tipo de datos size t es un n mero natural (sin signo). Cuando llamamos a estas funciones y les pasamos como par metros varibles de tipo entero (short, int o long), se realiza una conversi n a o de tipo de forma autom tica. a Junto con estas funciones usaremos el operador de C sizeof(tipo de datos). Este operador a retorna el n mero de bytes que ocupa una variable del tipo tipo de datos, tanto si este tipo est u predenido en C (int, float, etc.) como si es un tipo denido por el programador. Veamos algunos ejemplos que ilustran el empleo de memoria din mica. a El siguiente ejemplo gestiona de forma din mica dos vectores. a #include <stdio.h> #include <stdlib.h> typedef struct

f g

long DNI; char nom[256]; Tpersona;

void main( ) int i, lon; double *nota; Tpersona *alumno;

Los autores, 2000; Edicions UPC, 2000.

131

B. La librera est ndar a

do

f g

printf( "Cuntos alumnos hay?nn" ); a scanf( "%d", &lon ); while( lon < 0 );

nota = malloc( lon*sizeof(double) ); alumno = malloc( lon*sizeof(Tpersona) ); if ((nota == NULL) || (alumno == NULL))

f g

printf( " No hay memoria suficientenn" ); exit(0);

... /* Introduccin de datos y notas de cada alumno. o ...

*/

for (i= 0; i< lon; i++) printf( "Alumno:%d nombre:%s DNI:%ld nota:%lfnn", i, alumno[i].nom, alumno[i].DNI, nota[i] ); free( alumno ); free( nota );

Hay varios puntos a destacar: Notar que en la declaraci n de variables no declaramos ning n vector. En su lugar declaramos o u punteros. En este caso un puntero al tipo double y otro al tipo Tpersona. En estos punteros almacenamos las direcciones de memoria que devuelve malloc. Para indicarle a la funci n malloc qu cantidad de bytes de memoria necesitamos, hemos o e usado el operador sizeof. Los bytes requeridos son el n mero de elementos multiplicado por u el tama o en bytes de cada elemento. Cabe notar que sizeof se puede usar tambi n con tipos n e denidos por el programador como el tipo Tpersona. Despu s de llamar a malloc comprobamos que los punteros no sean NULL. Si lon es muy e grande, puede ocurrir que el computador no tenga memoria suciente. Esta comprobaci n de o error siempre debe hacerse. Cuando tenemos los bloques de memoria ya reservados, podemos acceder a ellos a trav s de los e punteros. En este caso, la notaci n para acceder a trav s de los punteros es id ntica a la que se o e e usa con vectores. Pero no debemos olvidar que alumno y nota son punteros. En el momento en que los bloques de memoria ya no son necesarios, debemos liberarlos. Para ello usamos la funci n free. Cualquier intento de acceder a los bloques de memoria despu s o e de la llamada a free generara un error de ejecuci n. o

Los autores, 2000; Edicions UPC, 2000.

B.7. Memoria din mica a

132

En este ejemplo multiplicamos la matriz A por el vector x dejando el resultado en el vector y. #include <stdio.h> #include <stdlib.h>

void main( ) int i, j, nfil, ncol; double *x, *y, *A; do

f g f g

printf( " Nmero de filas?nn" ); u scanf( "%d", &nfil ); while( nfil < 0 );

do printf( " Nmero de columnas?nn" ); u scanf( "%d", &ncol ); while( ncol < 0 );

A = malloc( nfil*ncol*sizeof(double) ); x = malloc( ncol*sizeof(double) ); y = calloc( nfil, sizeof(double) ); if ((x == NULL) || (y == NULL) || (A == NULL))

f g

printf( " No hay memoria suficientenn" ); exit(0);

... /* Introduccin del vector x y la matrix A */ o ... for (i= 0; i< nfil; i++)

f g

for (j= 0; j< ncol; j++) y[i] = y[i] + A[i*ncol+j] * x[j];

for (i= 0; i< nfil; i++) printf( "y[%d] = %lfnn", i, y[i] ); free( A );

Los autores, 2000; Edicions UPC, 2000.

133

B. La librera est ndar a

free( x ); free( y );

Los puntos m s destacables son los siguientes: a Para reservar memoria para el vector y utilizamos la funci n calloc. De esta forma el bloque o de memoria queda inicializado a cero y no es necesario inicializar cada componente de y en el algoritmo de multiplicaci n. o La notaci n para acceder a los bloques de memoria x e y a trav s de los punteros coincide con o e la que usaramos si fuesen vectores declarados de forma est tica. Pero no pasa lo mismo con la a matriz. Finalmente, en este ejemplo modicamos el tama o de un bloque de memoria, que previamnete n haba sido reservado mediante la funci n malloc. o #include <stdio.h> #include <stdlib.h>

void main( ) int lon1, lon2; double *vec; do

f g

printf( "Longitud del vector?nn" ); scanf( "%d", &lon1 ); while( lon1 < 0 );

vec = malloc( lon1*sizeof(double) ); if (vec == NULL)

f g

printf( " No hay memoria suficientenn" ); exit(0);

... /* Aqu trabajamos con vec */ ... do

printf( " Nueva longitud del vector?nn" ); scanf( "%d", &lon2 );

Los autores, 2000; Edicions UPC, 2000.

B.7. Memoria din mica a

134

while( lon2 < 0 );

vec = realloc( vec, lon2*sizeof(double) ); if (vec == NULL)

f g

printf( " No hay memoria suficientenn" ); exit(0);

... /* Aqu trabajamos con vec */ ...

free( vec );

La funci n realloc nos permite modicar el tama o del bloque de memoria reservado, pero no o n modica los datos almacenados en dicho bloque. Es decir: Si lon2 < lon1, tendremos un bloque de memoria m s peque o, pero los lon2 valores a n almacenados seguir n siendo los mismos. a Si lon2 > lon1, tendremos un bloque de memoria m s grande. Los primeros lon1 vaa lores ser n los mismos que haba antes de la llamada a realloc, mientras que los lon2 a lon1 valores nales ser n aleatorios (no estar n inicializados). a a

Los autores, 2000; Edicions UPC, 2000.

135

C. Sistemas de numeraci n o

Ap ndice C e

Sistemas de numeraci n o
Un computador usa el sistema de numeraci n en base 2 debido a c mo funcionan los dispositivos o o electr nicos b sicos (los transistores) que lo forman. En el sistema de numeraci n en base dos s lo o a o o existen 2 cifras el 0 y el 1. A las cifras de un n mero en base 2 se las denomina bits. A un grupo de 8 u bits se le denomina byte.

C.1 Naturales
Los n meros naturales se representan mediante un c digo llamado binario natural, que consiste simu o u plemente en expresar el n mero en base 2. Si disponemos n bits para representar n meros naturales u n combinaciones que se muestran en la tabla C.1. tendremos las 2

C.2 Enteros
Los n meros enteros se representan mediante un c digo llamado complemento a 2. Este c digo se usa u o o porque simplica notablemente la construcci n de los circuitos electr nicos necesarios para realizar o o operaciones aritm ticas, fundamentalmente sumas y restas. e El complemento a 2 de un n mero se calcula de la siguiente forma: si el n mero es positivo su u u complemento a 2 coincide con su expresi n en binario natural; si el n mero es negativo se debe escribir o u la representaci n en binario natural de su m dulo (valor absoluto), complementar a 1 dicha expresi n o o o Tabla C.1: Representaci n de n meros naturales en binario natural o u Valor Decimal
0 1 2 3 4

n;1

...

Binario Natural 0.....0 0....01 0...010 0...010 0...100 ... 1.....1

Los autores, 2000; Edicions UPC, 2000.

C.3. Reales

136

Tabla C.2: Representaci n de n meros enteros en complemento a 2 o u Valor Decimal

;2n; ;
n;
...
1 0 1

...
1

+2

Complemento a 2 10....0 ... 1.....1 0.....0 0....01 ... 01....1

(cambiar los 0 por 1 y viceversa), y nalmente sumarle 1. Por ejemplo si disponemos de 4 bits para representar n meros enteros y deseamos representar el n mero ;3, tendramos que: u u 3 = 0011 1100 + 1 -3 = 1101 En general, si disponemos de muestran en la tabla C.2. Complemento a 1 Complemento a 2

n bits, podemos representrar los n meros en complemento a 2 que se u

La codicaci n en complemento a 2 tiene algunas propiedades interesantes como las siguientes: o El cero tiene una unica representacion: 0...0 = 1...1 + 1 Todos los n meros del mismo signo empiezan por el mismo bit: 1 para los negativos, 0 para los u positivos La suma/resta de numeros en complemento a 2 se puede realizar mediante la suma binaria bit a bit, despreciando el ultimo acarreo. Por ejemplo: -2 - 1 = -3 2 - 1 = 1 1....10 0...010 + + 1...1 1...1 = = 1...10 0...01

C.3 Reales
Los n meros reales se representan mediante un c digo llamado coma otante. Este c digo es simpleu o o mente la representaci n en notaci n cientca normalizada y en base 2. o o Recordemos que un numero en notaci n cientca se representa mediante la siguiente expresi n: o o Donde la mantisa es un n mero real con la coma decimal colocada u a la derecha o a la izquierda de la primera cifra signicativa (normalizaci n por la derecha o por la o o izquierda); la base es la misma que la base de numeraci n usada para representar mantisa y exponente; u u y el exponente es un n mero entero. Los siguientes ejemplos muestran n meros representados en notaci n cientca en base 10, normalizados por la derecha y por la izquierda: o

mantisa baseexponente.

Los autores, 2000; Edicions UPC, 2000.

137

C. Sistemas de numeraci n o

Tabla C.3: Representaci n de n meros enteros en exceso 2e;1 o u Valor Decimal

;2e; ;
e;
...
1 0 1

...
1

+2

Complemento a 2 10....0 ... 1.....1 0.....0 0....01 ... 01....1


1

exceso 2e;1 0.....0 ... 01....1 10....0 10...01 ... 11....1

3 141592

2 53547

10

10
3

= =

0 3141592 10

0 253547

10
2

La notaci n cientca usada por los computadores tiene algunos detalles especiales debidos a que o usa la base 2. 1. En la memoria unicamente se almacena: una secuencia de m bits que representa la mantisa, una secuencia de e bits que representa el exponente, el signo de la mantisa se almacena usando 1 bit (0 signica positivo y 1 signica negativo). La base no es necesario almacenarla, ya que siempre es 2. Si pudi semos ver un n mero real e u almacenado en la memoria del computador veramos una secuencia de bits como la siguiente:

signo

bit bits bits z}|{ z m}| { ze }| { : |{z} | :{z: } | {z }


1

101

010

mantisa

exponente

10010

2. En base 2 s lo existen 2 cifras, el 0 y el 1. As pues, el primer bit signicativo siempre ser un 1, o a por lo que este primer bit no se almacena en la memoria. Los circuitos electr nicos que operan o con datos en coma otante ya tienen en cuenta que delante de todos los bits de la mantisa siempre hay un 1. A este bit que no se almacena se le denomina bit oculto o implcito. u 3. El exponente se almacena usando un c digo llamado exceso 2e;1 , donde e es el n mero o de bits usados para almacenar el exponente. Este c digo proviene de rotar de forma cclica o e;1 posiciones la tabla del complemento a 2 (ver Tab. C.3). 2 Para calcular un c digo en exceso podemos usar la siguiente f rmula: o o Valor decimal
=

Valor en binario natural

e;

Notar que en el c digo en exceso todos los n meros negativos comienzan por 0 y todos los o u n meros positivos comienzan por 1. Adem s, a valores crecientes les corresponden c digos que u a o ledos en binario natural tambi n son crecientes. Es decir: ;2 < 1 , 01 : : : 10 < 10 : : : 01. e De esta forma podemos comparar n meros de un c digo en exceso usando un simple comparador u o de n meros en binario natural. Sin embargo, los circuitos para sumar/restar n meros codicados u u en exceso, ya no ser n un simple sumador binario. Por razones hist ricas se decidi usar el a o o c digo en exceso en vez del c digo en complemento a 2 para representar los exponentes. o o

Los autores, 2000; Edicions UPC, 2000.

C.3. Reales

138

Tabla C.4: Representaci n de n meros reales o u Valor Decimal Signo

NAN M nimo ::: M aximo

x x x ::: x

Mantisa

::: x:::x ::: ::: :::


0 0 0 0 1 1

Exponente
0 0 0

::: ::: ::: ::: :::

0 0 1

4. Debido al uso del bit oculto, el n mero cero no se puede representar, ya que sea cual sea la manu tisa, esta nunca ser cero. Dado que el n mero cero es un n mero importante, que es necesario a u u poder representar, se hace una excepci n a la regla de representaci n. De forma arbitraria se o o decide que el n mero cero se representa mediante la combinaci n en que todos los bits de la u o mantisa y el exponente son ceros. 5. Adem s, las diferentes combinaciones de mantisa con el exponente cuyo c digo son todo ceros a o tampoco se usan como n meros. Estos c digos se reservan como c digos de error, generados u o o por los circuitos aritm ticos cuando se producen condiciones de error como: una divisi n en que e o el divisor es cero, la raz cuadrada de un n mero negativo, etc. A estos c digos de error se les u o denomina NAN (del ingles, Not A Number). Por lo tanto:

El n mero representable m s cercano a cero es 1:0 u a toda su mantisa con ceros y el exponente es la combinaci n o

e; El exponente m ximo es +(2e; a


El exponente mnimo es

(2

1 1

; ;

1) 1)

y se representa mediante la combinaci n o , y se representa mediante la combinaci n o


0

Exp:M nimo . Este n mero tiene 2 u

::: :::

01 1

El n mero representable m s grande es u a ) tiene toda su mantisa con unos y el exponente es la combinaci n o

;m 2(1 ; 2
(

:::

+1)

Exp:M aximo . Este n mero 2 u


1

01

:::

En la tabla C.4 se muestran ordenados los c digos de un formato de coma otante. El smbolo o x signica cualquier bit 0 o 1. 6. Cabe notar que cuantos m s bits se usen para representar la mantisa, mayor precisi n se tiene en a o la representaci n (menor error relativo). Cuantos m s bits usemos para representar el exponente o a mayor rango num rico abarcaremos. e Si cada computador usase su propio sistema de representaci n en coma otante, podramos tener o valores diferentes al ejecutar el mismo programa en computadores diferentes. Para evitar este problema, la representaci n en coma otante est estandarizada por la organizaci n IEEE (Institute of Electrical o a o and Electronics Engineers) bajo el est ndar IEEE-754. Actualmente todos los computadores cumplen a con dicho est ndar. a

C.3.1 Problemas derivados de la representaci n en coma otante o


Para representar n meros reales en un computador siempre usaremos una secuencia nita de bits. En u consecuencia, nunca podremos representar todos los n meros. Al subconjunto de n meros que podeu u mos representar en un formato coma otante se le denomina rango de representaci n. o

Los autores, 2000; Edicions UPC, 2000.

139

C. Sistemas de numeraci n o

Debido a que el rango de representaci n es nito, las operaciones aritm ticas no tienen las propieo e dades habituales. Por ejemplo, la suma ya no es asociativa. Supongamos un formato de coma otante donde el n mero m ximo representable es el 8:0. Entonces tendremos que: u a
(8 0 + 2 0)

8 0 + (2 0

: ; : 6 : : ; : : ; : : ; : ) ERROR : ; : : ; : : ) CORRECTO
(8 0 + 2 0)

4 0 = 8 0 + (2 0 4 0

4 0)

4 0 = 10 0

4 0) = 8 0

2 0 = 6 0

A esta situaci n se la denomina overow (desbordamiento por arriba), y ocurre cuando una operao ci n genera un n mero mayor que el m ximo representable. Adem s del overow puede producirse el o u a a underow (desbordamiento por abajo) cuando el resultado de una operaci n cae en la zona de n meros o u situados entre 0 y el n mero mnimo representable. u En conclusi n, al realizar c lculos en coma otante deberemos tener en cuenta el orden en el que o a se realizan las operaciones, para evitar las citadas situaciones de error.

Los autores, 2000; Edicions UPC, 2000.

C.3. Reales

140

Los autores, 2000; Edicions UPC, 2000.

141

D. Tabla de caracteres ASCII

Ap ndice D e

Tabla de caracteres ASCII


Adem s de poder representar valores num ricos, es preciso que un ordenador sea capaz de almacenar a e los signos alfanum ricos que usamos normalmente en el lenguaje humano. Para ello se usa un c digo de e o 8 bits llamado c digo ASCII. Cada combinaci n de este c digo representa un car cter. Los perif ricos, o o o a e como la pantalla o una impresora, son capaces de reconocer estos c digos y mostrar/imprimir el smbolo o correspondiente. Entre los caracteres representados adem s de los smbolos que usamos para escribir (letras, cifras, a signos de puntuaci n, etc) tambi n hay caracteres de control, necesarios para el funcionamiento de los o e perif ricos. Por ejemplo, el car cter nn provoca un salto de lnea cuando es mostrado en una pantalla. e a Originalmente el c digo ASCII s lo usaba 7 bits para codicar caracteres, el octavo bit (bit de pao o ridad) se usaba para control de errores. Por lo tanto, el c digo ASCII original s lo poda representar o o 7 128 = 2 caracteres diferentes. Actualmente, este control de errores no es necesario debido al perfeccionamiento de los equipos, por ello el octavo bit puede ser usado tambi n para codicar caracteres. A e este c digo se le conoce como ASCII extendido y codica 256 = 28 caracteres. o La tabla D.1 muestra los primeros 128 caracteres de acuerdo con la codicaci n est ndar ASCII. o a

Los autores, 2000; Edicions UPC, 2000.

142

Tabla D.1: Caracteres y sus c digos ASCII o ASCII 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 char NUL n0 SOH STX ETX EOT ENQ ACK BEL na BS nb HT nt LF nn VT nv FF nf CR nr SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US ASCII 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 char SP ! # $ % & ASCII 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 char @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ ASCII 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 char a b c d e f g h i j k l m n o p q r s t u v w x y z

( )
+

;
. / 0 1 2 3 4 5 6 7 8 9 : ; = ?

n
]

DEL

Los autores, 2000; Edicions UPC, 2000.

143

E. Bibliografa y recursos WEB

Ap ndice E e

Bibliografa y recursos WEB


El Lenguaje de programaci n C o Brian W. Kernighan y Dennis M. Ritchie Prentice Hall, 1992, segunda edici n. o http://cm.bell-labs.com/cm/cs/cbook/index.html De obligada referencia, es el primer libro sobre lenguaje C. Uno de los autores, Dennis M. Ritchie, es uno de los creadores del lenguaje. Incluye la denici n ocial de C y un o gran n mero de ejemplos interesantes, aunque en algunos aspectos el material presentado u es obsoleto. Presupone que el lector est familiarizado con la programaci n de sistemas. a o Programaci n en C o Byron Gottfried Mc Graw Hill, 1997, segunda edici n. o Extenso y eshaustivo libro con multitud de ejemplos detallados. Est estructurado como a un libro de texto, por lo que contiene gran cantidad de ejercicios, cuestiones de repaso, etc. Para principiantes. The Annotated ANSI C Standard Herbert Schildt Osborne - Mc Graw Hill, 1993. El est ndar ANCI C comentado y anotado por Herbert Shildt, miembro observador del a comit encargado de desarrollar el est ndar. ANSI C determina las reglas fundamentales e a que todo programador en C debera observar para crear programas funcionales y portables. Para lectores avanzados. C by example Greg M. Perry Que Corporation, 1993. Interesante libro que ense a a programar en C, construyendo programas paso a paso, desde n el primer momento. Separa los conceptos complicados del lenguaje en varios captulos cortos, de f cil asimilaci n. Incluye gran cantidad de ejercicios. Para principiantes. a o

Los autores, 2000; Edicions UPC, 2000.

144

The Development of the C Language Dennis M. Ritchie History of Programming Languages Conference (HOPL-II), 1993. http://cm.bell-labs.com/cm/cs/who/dmr/chist.html Interesante artculo de divulgaci n que describe la historia de la creaci n del lenguaje C, o o all por los a os 70. a n WEB: Programming in C http://www.lysator.liu.se/c/index.html Un sitio WEB muy interesante. Incluye multitud de enlaces a recursos en Internet sobre programaci n en C, historia del lenguaje, curiosidades, etc. o WEB: Frequently Asked Questions about C http://www.eskimo.com/ scs/C-faq/top.html En este sitio WEB encontraremos una recopilaci n de las preguntas m s habituales sobre o a programaci n en lenguaje C del grupo de noticias en Internet comp.lang.c . o

Los autores, 2000; Edicions UPC, 2000.

You might also like