Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Intro Ducci On A Lengua Je C
Intro Ducci On A Lengua Je C
Introduccin a
la programacin en C
EDICIONS UPC
Produccin:
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
vii
Prefacio
ix
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
2
2
3
3
4
5
5
5
6
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
7
7
8
9
10
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
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13
13
13
14
14
15
16
17
17
17
.
.
.
.
.
Indice General
ii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
18
19
19
20
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 . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
23
23
25
26
27
28
31
.
.
.
.
.
.
.
33
33
35
36
38
38
38
39
3.5
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 . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
41
41
42
43
44
44
45
45
46
47
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
49
50
50
52
53
54
54
55
55
56
57
.
.
.
.
.
.
.
.
.
Indice General
iii
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
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
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
87
87
89
89
90
91
91
91
91
92
93
94
94
95
Indice General
iv
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 96
. 99
. 100
. 101
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 . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
105
107
109
111
111
113
113
113
116
A El preprocesador
A.1 Directiva include . . . . . .
A.2 Directivas define y undef .
A.3 Directivas ifdef y ifndef .
A.4 Macros . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
119
119
119
120
122
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
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
123
123
124
124
124
126
127
128
129
129
129
.
.
.
.
135
135
135
136
138
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
141
143
Indice de Figuras
Indice de Figuras
1.1
1.2
1.3
1.4
1.5
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
2.1
Modelo de compilaci n de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
o
11
4.1
4.2
24
29
5.1
5.2
5.3
34
35
36
7.1
7.2
7.3
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
50
54
56
9.1
82
99
Indice de Tablas
vii
Indice de Tablas
3.1
3.2
3.3
3.4
3.5
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
6.1
6.2
6.3
6.4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
44
45
46
8.1
71
C.1
C.2
C.3
C.4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
135
136
137
138
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
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
Captulo 1
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
Programador
Lenguaje natural
Lenguaje de programacin
Lenguaje mquina
Ordenador
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
que se combinan entre s siguiendo las reglas de una sintaxis predenida, con el n de posibilitar la
COBOL
FORTRAN
1950
Algol
1955
Prolog
SIMULA
BASIC
PL/I
LISP
Ensamblador
Forth
1960
Logo
1965
Ada
C
Modula-2
Smalltalk C++
Pascal
1970
1975
1980
1985
Java
1990
1995
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.
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
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
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.
Instruccin 1
Intrprete
Ejecucin 1
Instruccin 2
Intrprete
Ejecucin 2
Instruccin 3
Intrprete
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
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. Traductores
ERRORES
Edicin
Compilacin
Montaje
Fuente
Ejecucin
Objeto
Ejecutable
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
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.
Captulo 2
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
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.
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
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
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
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:
10
/* Mi primer programa en C */
void main()
f
g
/* ...
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
nn"
);
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
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.
11
Cdigo fuente
Preprocesador
Compilador
Cdigo objeto
Libreras
Montador
Cdigo ejecutable
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.
12
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
14
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
; =
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.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
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
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
16
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
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
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
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
17
3. Empezando a programar
Binarios
Signo negativo
Incremento
Decremento
Suma
Resta
Multiplicaci n
o
Divisi n
o
M dulo
o
;
++
;;
+
;
=
correspondencia entre el formato y la lista de valores, tanto en n mero como en el tipo de los mismos.
u
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
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. Expresiones
18
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
op =
expresin2
o
que es equivalente a:
expresin1 = expresin1
o
o
op
expresin2
o
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
19
3. Empezando a programar
<
>
<
>
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
!
! =
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.
jj
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).
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).
3.5. Ejercicios
20
Smbolo
Asociatividad
Izquierda a derecha
Derecha a izquierda
()
;
++
;;
Izquierda a derecha
=
%
Izquierda a derecha
<
<
>
>
Izquierda a derecha
Izquierda a derecha
==
! =
&&
jj
=
=
+ =
Izquierda a derecha
Izquierda a derecha
Derecha a izquierda
%=
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
;2
(a) y
(b) y
+ =
(c) y
(y
(d) y
y++
;;x;
2;
==
x);
x;
20 % 6
2
15
21
3. Empezando a programar
(c) 5
(d) 8
15
==
(e) (4
16
3
<
jj
(4
2)
7 != 4 && 4
jj
>
<
2) && 3
<
12
3.5. Ejercicios
22
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
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:
4.1. Construcci n if
o
24
Cierto
Cierto
Condicin
Grupo de
sentencias
Grupo de
sentencias 1
(a)
Falso
Condicin
Grupo de
sentencias 2
(b)
sentencia 1;
sentencia 2;
. . .
sentencia N;
int a;
scanf("%d", &a);
if (a % 2 == 0) /* Comprobar si a es par.
a = a + 1;
printf( "Ahora es impar: %dnn", a );
*/
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).
25
4. Construcciones condicionales
f
g
grupo de sentencias 1;
else
f
g
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;
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;
4.1. Construcci n if
o
26
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;
. . .
f
g
grupo de sentencias 1;
else if (condicin 2)
o
f
g
grupo de sentencias 2;
. . .
else if (condicin N)
o
f
g
grupo de sentencias N;
else
f
g
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
int hora;
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;
o
if (a > b)
c = a;
else
c = b;
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) );
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.
29
4. Construcciones condicionales
Expresin
Grupo de
sentencias 1
Grupo de
sentencias 2
...
Grupo de
sentencias N
Grupo de
sentencias
por defecto
f
g
grupo de sentencias 1;
grupo de sentencias 2;
g. . .
else if (expresin == constante N)
o
f
g
grupo de sentencias N;
else
f
g
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 )
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;
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
a = c;
b = 0;
else
f
g
g
c += d;
c = (c == 0) ?
b = a + c + d;
(c + b) :
(c - a);
A y ;y
=
y B y
=
x ;x ;x
2
1)
y ;y
1)
4.4. Ejercicios
32
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
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
34
Falso
Condicin
Cierto
Grupo de
sentencias
bada en ;1:
#include <stdio.h>
void main()
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
35
5. Construcciones iterativas
Grupo de
sentencias
Condicin
Cierto
Falso
...
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.
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).
en una secuencia de
36
Sentencia inicial
Falso
Condicin
Cierto
Grupo de sentencias
Incremento / Decremento
37
5. Construcciones iterativas
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
X
10
El programa del siguiente ejemplo utiliza la construcci n for para calcular el sumatorio
o
=1
#include <stdio.h>
void main()
f
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 */
38
a
o
a
for (i = 0, j = 10 ; i < 10, j > 0 ; i++, j-=2)
f
g
/* Grupo de sentencias */
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.
f
g
sentencia;
incremento/decremento;
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:
39
5. Construcciones iterativas
do
scanf("%d", &num);
if (num < 0)
f
g
*/
f
g
. . .
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
= 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.
5.5. Ejercicios
40
41
Captulo 6
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
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;
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
+
42
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
lista de variables;
O simplemente:
short
lista de variables;
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
lista de variables;
O simplemente:
long
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:
43
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
lista de variables;
O simplemente:
int
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
lista de variables;
O simplemente:
unsigned
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
6.2. Caracteres
44
Rango
2147483648 2147483647]
0 4294967295]
32768 32767]
0 65535]
63
0 2
63
2
64
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
45
C digo
o
na
nn
nf
nr
nt
n
n"
n0
Rango
;
;
128 127]
128 127]
0 255]
Tama
no
1 byte
1 byte
1 byte
Formato
%c
%d
%u
Signicado
pitido
salto de lnea
salto de p gina
a
principio de la misma lnea
tabulador
ap strofe
o
comillas
car cter nulo
a
tambi n los modicadores signed y unsigned. Los rangos de valores se muestran en la tabla 6.3.
e
int cod;
char c;
. . .
cod = 98;
c = (char) cod;
. . .
46
:
:
Rango
E;
E
E;
E
1 1754945
:
:
8 bytes
%f, %e, %g
%f, %e, %g
+ 38
2 225073858507202
308
1 797693134862312
long double
Formato
%f, %e, %g
16 bytes
38
3 4028232
Tama
no
4 bytes
+ 308
: :::E ;
: :::E
8 4
4932
5 9
+ 4932
0 9]
c = (char)((int)0 + i);
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
, mientras que
. Expresando el error
1000
47
k
X
i
=1
i ;
k;
X
=1
i j<
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));
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.
6.4. Ejercicios
48
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
49
Captulo 7
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
7.1. Vectores
50
N-1
...
Primer elemento
del vector
ltimo elemento
del vector
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];
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
51
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;
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.
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 ()
g
g
if (num == 0) c0++;
if (num == 1) c1++;
if (num == 2) c2++;
if (num == 3) c3++;
scanf( "%d", &num );
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,
g
g
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
53
#include <math.h>
#include <stdio.h>
#define MAX 10
void main()
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
7.2. Matrices
54
M-1
0
1
...
...
Primer elemento
de la matriz
...
...
...
...
ltimo elemento
de la matriz
...
N-1
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
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
int mat[3][4] =
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
55
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()
f
g
/* Traspuesta */
for (i= 0; i< filas; i++)
for (j= 0; j< columnas; j++)
matrizT[j][i] = matriz[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.
56
M-1
...
...
...
Primer elemento
de la tabla
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
ltimo elemento
de la tabla
...
...
N-1
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()
57
*/
f
g
g
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
"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.
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
=
=
=
=
=
=
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;
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
59
"Una frase" U n a
10
11
10
11
\0
cadena[0] = \0;
0
"" \0 n a
\0
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
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;
/* La que buscamos. */
/* Una palabra de la frase.
*/
printf( "nnPALABRA:" );
scanf( "%s", pal );
printf( "nnFRASE:" );
c = ;
while (c != nn)
f
g
g
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()
61
if ((cad3[i] >= a)
cad3[i] -= delta;
else if ((cad3[i] >=
cad3[i] += delta;
i++;
g
g
%s
nn",
cad3 );
7.5 Ejercicios
1. D nde est el error en el siguiente programa?
o
a
void main()
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
u
5. Escribir un programa que decida si una palabra es palndroma o no. La palabra se almacena en
7.5. Ejercicios
62
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
63
Captulo 8
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;
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
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
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;
65
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 =
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
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
g;
char nombre[20];
char apellido[20];
long int dni;
struct tablapersonas
g;
int numpersonas;
struct datos persona[100];
void main()
int i;
struct tablapersonas tabla;
printf( "Nmero de personas: " );
u
scanf( "%d", &tabla.numpersonas );
for (i= 0; i< tabla.numpersonas; i++)
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
67
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;
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
g;
struct helicoptero
g;
struct carguero
g;
union aeroplano
g;
68
struct un aeroplano
g;
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
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
Tambi n puede asignarse el mismo valor num rico a diferentes constantes, as como dejar que el
e
e
69
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
void main()
. . .
if (dia <= VIERNES)
printf( "Es laborable" );
. . .
dia = MARTES;
/* Muestra el valor de dia */
printf( "Hoy es: %d", dia );
. . .
utilizaci n en un programa:
o
HOMBRE, MUJER
g;
70
typedef struct
f
g
void main()
Tpersona paciente;
. . .
scanf( "%f", &paciente.alt );
gets( paciente.nombre );
printf( "%s", paciente.apellido );
paciente.sex = MUJER;
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
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 );
71
y
0
1
0
1
AND (&)
0
0
0
1
OR ( j )
0
1
1
1
XOR (b)
0
1
1
0
a= 0x5b3c
a= 23356
a= 23356
b= 0xa4c3
b= 42179
b= -23357
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.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 >>
c;
c;
c;
c;
/*
/*
/*
/*
/*
/*
/*
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 */
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;
73
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
f
g
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.
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:
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.
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
77
9. Punteros
double num;
double *pnum = NULL;
. . .
pnum = #
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 *
variable
/* Se usa *
variable
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,
78
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;
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
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 .
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
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:
*/
81
9. Punteros
*/
f
g
*/
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
82
Puntero pt
Fila 0
...
A[0][0]
i*m elementos
A[0][m-1]
...
i*m+j elementos
A[i][0]
...
j elementos
A[i][j]
...
Fila i
...
A[i][m-1]
Fila n-1
...
A[n-1][0]
A[n-1][m-1]
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.
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];
typedef struct
f
g
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;
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
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.
85
9. Punteros
9.6. Ejercicios
86
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
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()
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;
void main()
89
10. Funciones
f
long int m, n;
float 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
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
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;
int i;
long int f = 1;
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.
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,
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
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
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
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
int func1()
f
g
int func2()
f
g
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.
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
93
10. Funciones
f
g
void main()
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.
int max;
f
f
if (x > y)
max = x;
else
max = y;
printf( "MAX=%d", max );
if (x > y)
g
g
printf( "MAX=%d", x );
return;
printf( "MAX=%d", y );
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) );
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.
f
g
x = x * x;
printf( "Dentro x = %dnn", x );
void main()
int x = 5;
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
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
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;
void main()
int 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.
97
10. Funciones
%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);
void main()
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++)
y[i] = 0.0;
for (i= 0; i< ncol; i++)
y[i] = y[i] + A[i*MAXCOL+j] * x[j];
void main()
...
/* 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,
99
10. Funciones
A
A[0]
M[0][0]
ncol=2
A[1]
M[0][1]
A[2]
M[0][2] MAXCOL = 3
A[3]
M[1][0]
A[4]
M[1][1]
A[5]
M[1][2]
A[6]
M[2][0]
A[7]
M[2][1]
A[8]
M[2][2]
i*MAXCOL+j = 2*3+1=7
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
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
10.6. Recursividad
100
o
a
void main( int argc, char *argv[] )
f
g
/* 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
else
f
g
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
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:
int fact( int x )
f
g
if (x <= 1)
return (1);
return (x * fact(x-1));
f
g
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 )
void main()
int i, vector[N];
10.7 Ejercicios
1. El c lculo de
a
ex
n
X xi
i i
n sucientemente
e
u
que permita calcular la potencia i- sima de un n mero x,
=0
para
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
sen x
(
1
X xi
i
i
=0
cos x
(
(2
+ 1)!
1
X xi
i ;
i
2
) =
=0
frac.
(2 )!
2 +1
) =
1)
1)
x = x +
*y = *y
x = x +
*y = *y
printf(
1;
+ 1;
a;
+ b;
"%d %dnn", x, *y );
a = a +
*b = *b
a = a +
*b = *b
printf(
1;
+ 1;
a;
+ *b;
"%d %dnn", a, *b );
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
103
10. Funciones
f
g
a = P;
b = Q;
return ((a<b)?(int)a:(int)b);
f
g
*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 );
int i, sum = 0;
void main()
f
g
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;
X.a = "cian";
X.b = "magenta";
X.c = "amarillo";
printf( "%s%s%snn", X.a, X.b ,X.c );
return();
void main()
g
(b)
X->a = "cian";
X->b = "magenta";
X->c = "amarillo";
printf( "%s%s%snn", X->a, X->b, X->c );
return();
void main()
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.
z }| {
31
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
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
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:
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
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:
108
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
. . .
/* 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)
f
g
", N );
109
11. Ficheros
. . .
fclose( fp );
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.
del vector en cada lnea. Finalmente, el programa escribe el vector en un chero de salida usando el
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 );
110
exit(0);
f
g
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
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 );
111
11. Ficheros
f
g
f
g
else
printf( "El vector tiene demasiados elementosnn" );
fclose( fp );
. . .
/* Aqu trabajamos con vec */
. . .
fp = fopen( nombreFichero, "a" );
if (fp == NULL)
f
g
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.
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)
f
g
", N );
lon = 0;
while (!feof(fp) && (lon < MAXELE))
g
g
kk = fscanf( fp,
if (kk == 1)
lon++;
if (!feof(fp) &&
printf( "Todo
cabe
"%d", &vec[lon] );
(lon == MAXELE))
el contenido del fichero no
en el vectornn" );
fclose( fp );
. . .
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
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
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( )
(s/n)" );
f
g
f
g
f
g
if (fo == stdout)
fprintf( fo, " N. filas = " );
115
11. Ficheros
f
g
if (fo == stdout)
fprintf( fo, " N. columnas = " );
fscanf( fi, "%d", &Ncolumnas );
if (Ncolumnas > MAXCOLUMNAS)
f
g
if (fo == stdout)
fprintf( fo, "A[%d][%d] = ", i, j );
k = fscanf( fi, "%lf", &A[i][j] );
if (k != 1)
f
g
if (fo == stdout)
fprintf( fo, "x[%d] = ", i );
k = fscanf( fi, "%lf", &x[i] );
if (k != 1)
f
g
y[i] = 0.0;
for (j= 0; j< Ncolumnas; j++)
y[i] = y[i] + A[i][j] * x[j];
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
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
Pedro Lpez
o
Avd. Europa
Tlf. 931 113 456
Andrs Villa
e
Tlf. 931 113 457
Badalona
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 la ultima letra de una palabra y un signo de puntuaci n no debe haber ning n blanco.
o
u
11.5. Ejercicios
118
Entre un signo de puntuaci n y la primera letra de una palabra debe haber un espacio en
o
blanco.
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
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
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
undef permite eliminar smbolos previamente denidos. El uso de estas directivas es el siguente:
#undef nombreSmbolo
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.
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.
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
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
...
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
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
o
distinto.
123
Ap ndice B
e
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
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.
124
125
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
[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
126
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
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.
127
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
) Escritura con formato en
int ungetc( int c, FILE *fich ) Devuelve el car cter c, ledo previamente, al
a
128
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
129
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.
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
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;
131
do
f
g
f
g
...
/* Introduccin de datos y notas de cada alumno.
o
...
*/
free( alumno );
free( nota );
132
f
g
do
f
g
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
...
/* Introduccin del vector x y la matrix A */
o
...
for (i= 0; i< nfil; i++)
f
g
133
free( x );
free( y );
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( )
f
g
f
g
...
/* Aqu trabajamos con vec */
...
do
134
f
g
...
/* 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
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
C.3. Reales
136
;2n;
...
0
1
...
n;
+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
Complemento a 1
Complemento a 2
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
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.
137
C. Sistemas de numeraci n
o
;2e;
Complemento a 2
10....0
...
1.....1
0.....0
0....01
...
01....1
...
0
1
...
e;
+2
3 141592
2 53547
10
10
=
=
0 3141592
0 253547
10
10
exceso 2e;1
0.....0
...
01....1
10....0
10...01
...
11....1
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:
bit
bits
bits
z}|{ z m}| { ze }| {
:
|{z} | :{z: } | {z }
1
signo
101
010
mantisa
10010
exponente
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;
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
C.3. Reales
138
Signo
x
x
x
:::
x
NAN
M nimo
:::
M aximo
Mantisa
:::
x:::x
:::
:::
:::
0
Exponente
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:
e;
El exponente m ximo es +(2e;
a
El exponente mnimo es
(2
1
1
;
;
1)
1)
:::
:::
01
;m
2(1 ; 2
(
+1)
:::
01
:::
En la tabla C.4 se muestran ordenados los c digos de un formato de coma otante. El smbolo
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
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
: ; : 6 :
: ; :
: ; :
: ; : ) ERROR
: ; :
: ; :
: ) CORRECTO
:
(8 0 + 2 0)
(8 0 + 2 0)
8 0 + (2 0
4 0 = 8 0 + (2 0
4 0 = 10 0
4 0) = 8 0
4 0)
4 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.
C.3. Reales
140
141
Ap ndice D
e
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
142
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
!
#
$
%
&
(
)
+
;
.
/
0
1
2
3
4
5
6
7
8
9
:
;
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
[
n
]
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
DEL
143
Ap ndice E
e
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.
144
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