Logo Studenta

Introduccion-prog--C

¡Este material tiene más páginas!

Vista previa del material en texto

AULA POLITÈCNICA / ETSETB
EDICIONS UPC
Marco A. Peña Basurto
José M. Cela Espín
Introducción a
la programación en C
Primera edición: septiembre de 2000
Diseño de la cubierta: Manuel Andreu 
© Los autores, 2000
© Edicions UPC, 2000
Edicions de la Universitat Politècnica de Catalunya, SL
Jordi Girona Salgado 31, 08034 Barcelona
Tel.: 934 016 883 Fax: 934 015 885
Edicions Virtuals: www.edicionsupc.es
E-mail: edicions-upc@upc.es
Producción: CPET (Centre de Publicacions del Campus Nord)
La Cup. Gran Capità s/n, 08034 Barcelona
Depósito legal: B-32.449-2000
ISBN: 84-8301-429-7
Quedan rigurosamente prohibidas, sin la autorización escrita de los titulares del copyright, bajo las san-
ciones establecidas en las leyes, la reproducción total o parcial de esta obra por cualquier medio o pro-
cedimiento, comprendidos la reprografía y el tratamiento informático, y la distribución de ejemplares de
ella mediante alquiler o préstamo públicos.
Introducción a la programación en C
Marco A. Peña
José M. Cela
Departament d’Arquitectura de Computadors
Universitat Politècnica de Catalunya
08034 Barcelona, España
marcoa@ac.upc.es
cela@ac.upc.es
19 de junio de 2000
i Índice General
Índice General
Índice de Figuras v
Índice de Tablas vii
Prefacio ix
1 Conceptos básicos de programación 1
1.1 Ordenador y periféricos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Bits, bytes y palabras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Lenguajes de programación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3.1 Lenguajes de bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.2 Lenguajes de alto nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Elaboración de un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5 Traductores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.1 Ensambladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.2 Intérpretes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.3 Compiladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2 Primer contacto con C 7
2.1 Un poco de historia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 Caracterı́sticas del lenguaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Creación de un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Primeros pasos con C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 El modelo de compilación de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3 Empezando a programar 13
3.1 Identificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Estructura de un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3 Variables y constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3.1 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3.2 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.3 Entrada y salida de valores . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.4 Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.4.1 Operador de asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.4.2 Operadores aritméticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Índice General ii
3.4.3 Operadores relacionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.4.4 Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.4.5 Prioridad de operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.5 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4 Construcciones condicionales 23
4.1 Construcción if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.1.1 Variante if-else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.1.2 Variante if-else-if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.2 El operador condicional ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.3 Construcción switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.4 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5 Construcciones iterativas 33
5.1 Construcción while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5.2 Construcción do-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.3 Construcción for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.3.1 El operador coma (,) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.3.2 Equivalencia for-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.4 Las sentencias break y continue . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.5 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
6 Tipos de datos elementales 41
6.1 Números enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6.1.1 Modificadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.1.2 Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
6.2 Caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.2.1 Caracteres especiales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.2.2 Enteros y el tipo char . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.2.3 Conversiones de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.3 Números reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.4 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
7 Tipos de datos estructurados: Tablas 49
7.1 Vectores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
7.1.1 Consulta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.1.2 Asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.1.3 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
7.2 Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
7.2.1 Consulta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7.2.2 Asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7.2.3 Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7.3 Tablas multidimensionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7.3.1 Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
7.4 Cadenas de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
iii Índice General
7.4.1 Asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
7.4.2 Manejo de cadenas de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . 59
7.4.3 Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
7.5 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8 Otros tipos de datos 63
8.1 Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
8.1.1 Declaración de variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
8.1.2 Acceso a los campos . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . 65
8.1.3 Asignación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
8.1.4 Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.2 Uniones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
8.2.1 Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.3 Tipos de datos enumerados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
8.4 Definición de nuevos tipos de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.5 Tiras de bits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
8.5.1 Operador de negación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
8.5.2 Operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
8.5.3 Operadores de desplazamiento de bits . . . . . . . . . . . . . . . . . . . . . . 71
8.6 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
9 Punteros 75
9.1 Declaración y asignación de direcciones . . . . . . . . . . . . . . . . . . . . . . . . . 75
9.1.1 Declaración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
9.1.2 Asignación de direcciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
9.2 Indirección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
9.3 Operaciones con punteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
9.4 Punteros y tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
9.5 Punteros y estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
9.6 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
10 Funciones 87
10.1 Generalidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
10.2 Definición y llamada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
10.2.1 Definición . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
10.2.2 Prototipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
10.2.3 Llamada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.3 Variables y parámetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.3.1 Variables locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.3.2 Variables globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.3.3 Parámetros formales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
10.4 Devolución de resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
10.5 Paso de parámetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
10.5.1 Paso de parámetros por valor . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
10.5.2 Paso de parámetros por referencia . . . . . . . . . . . . . . . . . . . . . . . . 95
Índice General iv
10.5.3 Las tablas y las funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
10.5.4 Parámetros en la función main . . . . . . . . . . . . . . . . . . . . . . . . . 99
10.6 Recursividad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
10.7 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
11 Ficheros 105
11.1 Abrir y cerrar ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
11.2 Leer y escribir en ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
11.3 Otras funciones para el manejo de ficheros . . . . . . . . . . . . . . . . . . . . . . . . 111
11.3.1 feof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
11.3.2 ferror . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
11.3.3 fflush . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
11.4 Ficheros estándar: stdin, stdout, stderr . . . . . . . . . . . . . . . . . . . 113
11.5 Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
A El preprocesador 119
A.1 Directiva include . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
A.2 Directivas define y undef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
A.3 Directivas ifdef y ifndef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
A.4 Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
B La librerı́a estándar 123
B.1 Manipulación de cadenas de caracteres . . . . . . . . . . . . . . . . . . . . . . . . . . 123
B.2 Entrada y salida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
B.2.1 Entrada y salida básica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
B.2.2 Entrada y salida con formato . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
B.2.3 Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
B.3 Funciones matemáticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
B.4 Clasificación y manipulación de caracteres . . . . . . . . . . . . . . . . . . . . . . . . 128
B.5 Conversión de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
B.6 Manipulación de directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
B.7 Memoria dinámica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
C Sistemas de numeración 135
C.1 Naturales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
C.2 Enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
C.3 Reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
C.3.1 Problemas derivados de la representación en coma flotante . . . . . . . . . . . 138
D Tabla de caracteres ASCII 141
E Bibliografı́a y recursos WEB 143
v Índice de Figuras
Índice de Figuras
1.1 Niveles de abstracción en los lenguajes de programación . . . . . . . . . . . . . . . . 2
1.2 Cronologı́a en el desarrollo de algunos lenguajes de programación . . . . . . . . . . . 3
1.3 Ciclo de vida de un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Fases en la interpretación de un programa . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5 Fases en la compilación de un programa . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.1 Modelo de compilación de C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
4.1 Esquema de funcionamiento de if y de if-else . . . . . . . . . . . . . . . . . . . 24
4.2 Esquema de funcionamiento de switch . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.1 Esquema de funcionamiento de while . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.2 Esquema de funcionamiento de do-while . . . . . . . . . . . . . . . . . . . . . . . 35
5.3 Esquema de funcionamiento de for . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
7.1 Representación gráfica de un vector . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.2 Representación gráfica de una matriz . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7.3 Representación gráfica de una tabla de tres dimensiones . . . . . . . . . . . . . . . . . 56
9.1 Acceso a una matriz mediante un puntero . . . . . . . . . . . . . . . . . . . . . . . . 82
10.1 Acceso a una matriz mediante un puntero . . . . . . . . . . . . . . . . . . . . . . . . 99
11.1 Almacenamiento de un fichero de texto . . . . . . . . . . . . . . . . . . . . . . . . . 106
vii Índice de Tablas
Índice de Tablas
3.1 Palabras reservadas de C . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . 13
3.2 Operadores aritméticos en C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.3 Operadores relacionales y lógicos en C . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.4 Tabla de verdad de los operadores lógicos en C . . . . . . . . . . . . . . . . . . . . . 19
3.5 Prioridad y asociatividad de los operadores en C . . . . . . . . . . . . . . . . . . . . . 20
6.1 Representación de enteros en decimal, octal y hexadecimal . . . . . . . . . . . . . . . 42
6.2 Resumen de tipos de datos enteros . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.3 Caracteres interpretados como enteros . . . . . . . . . . . . . . . . . . . . . . . . . . 45
6.4 Resumen de tipos de datos reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
8.1 Tabla de verdad de los operadores lógicos . . . . . . . . . . . . . . . . . . . . . . . . 71
C.1 Representación de números naturales en binario natural . . . . . . . . . . . . . . . . 135
C.2 Representación de números enteros en complemento a 2 . . . . . . . . . . . . . . . . 136
C.3 Representación de números enteros en exceso 2e�1 . . . . . . . . . . . . . . . . . . . 137
C.4 Representación de números reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
D.1 Caracteres y sus códigos ASCII . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
ix Prólogo
Prólogo
Este libro surge a partir de la experiencia docente de los autores en la asignatura Introducción a los
ordenadores de la Escola Tècnica Superior d’Enginyeria de Telecomunicació de Barcelona, de la Uni-
versitat Politècnica de Catalunya. Como su t́ıtulo indica, se trata de un texto de introdución a la pro-
gramación en lenguaje C. El libro pretende ceñirse a los aspectos fundamentales del estándar ANSI C
actual. Aunque hoy en dı́a existen otros lenguajes de programación muy populares como C++ o JAVA,
la comprensión de estos lenguajes exige un sólido conocimiento de las bases de programación en C.
El texto está concebido como un curso completo y por lo tanto debe ser leı́do de forma secuencial.
Al final de cada capı́tulo 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 ejem-
plos resueltos en el texto. El lector debe recordar que la programación es una técnica aplicada, igual que
tocar un instrumento musical, y por lo tanto requiere muchas horas de ensayo para ser dominada. Los
ejercicios propuestos son lo suficientemente simples como para no requerir conocimientos adicionales
de otras materias (matemáticas, fı́sica, contabilidad, etc).
Uno de los puntos más importantes para quien empieza a programar es adoptar desde el principio
un buen estilo de programación. Esto es, escribir las construcciones del lenguaje de una forma clara
y consistente. En este sentido, los ejemplos resueltos en el texto muestran un buen estilo básico de
programación, por lo que se recomienda al lector imitar dicho estilo cuando realice sus programas.
Los apéndices A y B son de lectura obligada antes de comenzar a desarrollar programas de comple-
jidad media. En dichos apéndices el lector se familiarizará con el uso del preprocesador y de la librerı́a
estándar. Ambas son herramientas fundamentales para desarrollar programas.
El apéndice C incluye una descripción de los formatos de datos en el computador. Este tema no
es propiamente de programción, pero la comprensión de dichos formatos ayuda al programador a en-
tender mejor conceptos básicos, como las operaciones de conversión de tipos. El lector puede leer este
apéndice en cualquier momento, aunque recomendamos leerlo antes del capı́tulo 6.
En las referencias bibliográficas se indican algunas direcciones web donde el lector podrá encontrar
preguntas y respuestas comunes de quienes se inician en el lenguaje C. Aśı mismo, el lector podrá
encontrar materiales adicionales sobre programación, historia del lenguaje, etc.
1 1. Conceptos básicos de programación
Capı́tulo 1
Conceptos básicos de programación
1.1 Ordenador y periféricos
Un ordenador sólo es capaz de ejecutar órdenes y operaciones muy básicas, tales como:
� Aritmética entera: sumar, restar, multiplicar, etc.
� Comparar valores numéricos o alfanuméricos
� Almacenar o recuperar información
Con la combinación de estas operaciones básicas, y gracias a su gran potencia de cálculo, el orde-
nador puede llevar a cabo procesos muy complejos. Sin embargo, en cualquier caso existe una estrecha
dependencia del ordenador con el programador. Es el programador quien indica a la máquina cómo y
qué debe hacer, mediante la lógica y el razonamiento previo, expresado en forma de un programa.
En definitiva, el ordenador sólo es capaz de aceptar datos de entrada, procesarlos y facilitar otros
datos o resultados de salida. Los datos se introducen u obtienen del ordenador mediante los periféricos
de entrada y salida. Éstos son los encargados de facilitar la relación entre el corazón del ordenador y el
mundo exterior, y en particular los usuarios de ordenadores. Dependiendo de su función particular, los
periféricos pueden clasificarse en:
Periféricos de entrada: cuya función es facilitar la introducción de datos y órdenes al ordenador: te-
clado, ratón, lápiz óptico, lector de código de barras, escáner, tableta digitalizadora, etc.
Periféricos de salida: cuya función es mostrar al exterior información almacenada en memoria o los
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 ordena-
dor: discos y cintas magnéticos, discos ópticos, etc.
Periféricos de comunicación: encargados de establecer y facilitar el intercambio de información entre
dos ordenadores: módem, tarjetas de red (Ethernet, Token Ring, RDSI, . . . ), etc.
1.2. Bits, bytes y palabras 2
Lenguaje máquina
Lenguaje natural
Programador
Ordenador
Lenguaje de programación
Figura 1.1: Niveles de abstracción en los lenguajes de programación
1.2 Bits, bytes y palabras
La unidad de memoria más pequeña en un ordenador se denomina bit (del inglés binary digit). Puede
tomar únicamente dos posibles valores: 0 o 1. En ocasiones, debido a la relación intrı́nseca con los
valores en las señales eléctricas en circuitos digitales, se dice que un bit está bajo o alto, o bien desco-
nectado o conectado. Como puede verse, no es posible almacenar mucha información en un solo bit.
Sin embargo, un ordenador posee cantidades ingentes de ellos, por lo que podŕıa decirse que los bits
son los bloques básicos con los que se construye la memoria del ordenador.
El byte, compuesto por ocho bits (algunos autores se refieren a esta unidad como octeto), es una
unidad de memoria más útil. Puesto que cada bit puede tomar el valor 0 o 1, en un byte pueden represen-
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
de la palabra puede ser mucho mayor, pero siempre formada por un número de bits, potencia de 2. En
cualquier caso, los ordenadores encadenan dos o más palabras de memoria con el fin de poder almacenar
datos complejos y, en general, de mayor tamaño.
1.3 Lenguajes de programación
Un lenguaje de programación podrı́a definirse como una notación o conjunto de sı́mbolos y caracteres
que se combinan entre sı́ siguiendo las reglas de una sintaxis predefinida, con el fin de posibilitar la
transmisión de instrucciones a un ordenador. Dichos sı́mbolos y caracteres sontraducidos internamente
a un conjunto de señales eléctricas representadas en sistema binario, es decir, sólo dos valores: 0 y 1.
Esta traducción es necesaria porque el procesador sólo entiende ese lenguaje, al cual nos referiremos
como lenguaje máquina.
3 1. Conceptos básicos de programación
1950 1955 1960 1965 1970 1975 1980 1985 1990 1995
FORTRAN
LISP
Algol
COBOL
BASIC
PL/I
Logo
SIMULA
Prolog
Forth
Modula-2
Smalltalk
Ada
Pascal
C
JavaC++Ensamblador
Figura 1.2: Cronologı́a en el desarrollo de algunos lenguajes de programación
1.3.1 Lenguajes de bajo nivel
Se incluyen en esta categorı́a aquellos lenguajes que por sus caracterı́sticas se encuentran más próximos
a la arquitectura del ordenador, como el lenguaje máquina y el lenguaje ensamblador.
Lenguaje máquina
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.
El lenguaje máquina se caracteriza por ser el único que es directamente inteligible por el ordenador,
puesto que se basa en la combinación de dos únicos sı́mbolos (0 y 1) denominados bits. Además cada
procesador posee su propio lenguaje máquina, por lo que un programa escrito en lenguaje máquina de
un procesador X no podrá, en principio, ejecutarse en un procesador Y.
Lenguaje ensamblador
Constituye una evolución del lenguaje máquina. Se basa en la utilización de mnemotécnicos, esto es,
abreviaturas de palabras que indican nombres de instrucciones. Para programar en lenguaje ensambla-
dor 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.
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
están optimizados para una arquitectura especı́fica. Sin embargo, esto último es un inconveniente, pues
causa que los programas no sean portables de un ordenador a otro con un procesador distinto.
1.3.2 Lenguajes de alto nivel
Se engloban aquı́ todos los lenguajes de programación que por sus caracterı́sticas se asemejan más al
lenguaje natural del programador. Algunos de los más conocidos son: FORTRAN, BASIC, Pascal,
Modula, C, Ada, Java, etc. (ver Fig. 1.2).
La caracterı́stica más importante de estos lenguajes es que son independientes de la arquitectura del
ordenador, por lo que un programa escrito en un lenguaje de alto nivel puede ejecutarse sin problemas
1.4. Elaboración de un programa 4
�������������
�������������
�������������
�������������
�������������
�������������
��������������
��������������
��������������
��������������
��������
��������
��������
��������
�������
�������
�������
�������
�����������
�����������
�����������
�����������
Análisis
Diseño
Codificación
Explotación
Mantenimiento
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
escrito en un lenguaje de alto nivel deberá traducirse a lenguaje máquina, de forma que pueda ser
entendido y ejecutado por el ordenador. Para ello cada tipo de ordenador deberá disponer de unos
programas especiales que realicen dicha traducción (ver Sec. 1.5).
1.4 Elaboración de un programa
El desarrollo de un programa para solucionar un determinado problema informáticamente puede resu-
mirse en el ya clásico concepto de ciclo de vida. Éste puede desglosarse en los siguientes pasos a seguir
secuencialmente: análisis, diseño, codificación, explotación y mantenimiento (ver Fig. 1.3).
Análisis
En la fase de análisis se estudia cuál es el problema a resolver y se especifican a muy alto nivel los
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
de proceder al diseño es muy importante haber comprendido correctamente los requerimientos del
problema.
Diseño
Una vez bien definido el problema y las ĺıneas generales para solucionarlo, se requiere una solución
adecuada a un conjunto de recursos determinado. Tanto f́ısicos: en qué ordenador va a funcionar la
aplicación, de qué tipo de periféricos se dispone . . . , como lógicos: qué sistema operativo se usará, qué
herramientas de desarrollo, qué bases de datos . . . Finalmente se diseñará un conjunto de algoritmos
que resuelvan los distintos subproblemas en que se haya dividido el desarrollo.
Codificación
Consiste en la traducción de los algoritmos diseñados previamente, utilizando el lenguaje y entorno de
desarrollo escogidos en la fase anterior. Será necesario realizar pruebas que garanticen al máximo la
calidad de los programas desarrollados. Entre otras cosas, que estén libres de errores.
La documentación generada en esta fase junto con la de las fases anteriores será muy útil en el
futuro para las eventuales actuaciones de mantenimiento.
5 1. Conceptos básicos de programación
Instrucción 1 Intérprete Ejecución 1
Instrucción 2 Intérprete Ejecución 2
Instrucción 3 Intérprete Ejecución 3
. . .
Figura 1.4: Fases en la interpretación de un programa
Explotación
Los diferentes programas desarrollados en la fase anterior se instalan en el entorno final de trabajo. Si
es necesario se instalarán también otras herramientas de utilidad, necesarias para el correcto funciona-
miento del sistema. Se debe proporcionar documentación, manuales de usuario, formación, etc.
Mantenimiento
En esta fase se realizarán correcciones al sistema desarrollado, bien para solventar errores no depura-
dos, bien para cambiar o añadir nuevas funcionalidades requeridas por el cliente. Dependiendo de la
importancia del caso, será necesario retomar el ciclo de vida a nivel de codificación, diseño o incluso
análisis (ver Fig. 1.3).
Cuanto mejor se haya documentado el desarrollo en las primeras fases del ciclo de vida, menor será
el tiempo necesario para llevar a cabo los distintos tipos de mantenimiento.
1.5 Traductores
Como ya se ha comentado, el único lenguaje directamente inteligible por el ordenador es el lenguaje
máquina. Por ello, si se programa usando lenguajes de alto nivel será necesario algún programa traduc-
tor. Éste, a su vez, será el encargado de comprobar que los programas estén escritos correctamente, de
acuerdo con la definición del lenguaje de programación empleado. Pueden distinguirse varios tipos de
traductores:
1.5.1 Ensambladores
Los programas ensambladores son los encargados de traducir a lenguaje máquina los programas escritos
en lenguaje ensamblador. La correspondencia entre ambos lenguajes es muy directa, por lo que los
ensambladores suelen ser programas relativamente sencillos.
1.5.2 Intérpretes
El objetivo de un intérprete es procesar una a una las instrucciones de un programa escrito en un lenguaje
de alto nivel. Para cada instrucción se verifica la sintaxis, se traduce a código máquina y finalmente se
ejecuta. Es decir, que la traducción y la ejecución se realizan como una sola operación (ver Fig. 1.4).
1.5. Traductores 6
ERRORES
Ejecución
Edición
Compilación
Montaje
Fuente
Objeto
Ejecutable
Figura 1.5: Fases en la compilación de un programa
La principal desventaja de los intérpretes es su lentitud para ejecutar los programas, pues es nece-
sario verificar la sintaxis y realizar la traducción en cada ejecución.
1.5.3 Compiladores
La función de un compilador consiste en traducir unprograma fuente escrito en un lenguaje de alto
nivel a su equivalente en código máquina (también llamado código objeto).
Mientras que un intérprete traduce y ejecuta al mismo tiempo cada una de las instrucciones, un
compilador analiza, traduce y posteriormente ejecuta todo el programa en fases completamente separa-
das (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
interpretado.
El proceso de compilación
Edición Consiste en escribir el programa fuente usando el lenguaje de programación seleccionado y su
grabación en un fichero. Para ello es necesario usar un programa editor, que puede o no formar
parte del entorno de desarrollo.
Compilación En esta fase se verifica la sintaxis del programa fuente y se traduce el programa a código
máquina (objeto). Si se producen errores, el compilador muestra información del tipo de error y
dónde se ha producido.
Montaje Consistente en la combinación de los diferentes módulos objeto y librerı́as del lenguaje para
crear un programa ejecutable. Esta fase se conoce también como linkado.
Ejecución En esta fase se invoca al programa de la manera adecuada dependiendo del sistema operativo
sobre el que vaya a funcionar.
Como nota final, cabe decir que todo lenguaje de programación puede ser tanto interpretado como
compilado. Sin embargo, dependiendo del las caracteŕısticas del lenguaje y del uso mayoritario a que
esté destinado, es normal asociar a cada lenguaje una forma de traducción particular. Por ejemplo, el
lenguaje BASIC es mayoritariamente interpretado, mientras que C es compilado.
7 2. Primer contacto con C
Capı́tulo 2
Primer contacto con C
2.1 Un poco de historia
El lenguaje de programación C fue desarrollado por Dennis Ritchie en los Laboratorios Bell de la
empresa de comunicaciones AT&T, en 1972. C fue creado inicialmente con un propósito muy concreto:
el diseño del sistema operativo UNIX. Sin embargo, pronto se reveló como un lenguaje muy potente y
flexible, lo que provocó que su uso se extendiese rápidamente, incluso fuera de los Laboratorios Bell.
De esta forma, programadores de todo el mundo empezaron a usar el lenguaje C para escribir programas
de todo tipo.
Durante años, el estándar de facto del lenguaje C fue el definido en el libro El lenguaje de pro-
gramación C, escrito por Brian Kernighan y Dennis Ritchie en 1978. Sin embargo, con el tiempo
proliferaron distintas versiones de C, que con sutiles diferencias entre ellas, empezaron a causar proble-
mas de incompatibilidad a los programadores. Con el fin de cambiar esta situación, el Instituto Nacional
de Estándares Americano (más conocido como ANSI) creó un comité en 1983 para establecer una de-
finición estándar de C que fuese no ambigua e independiente de la arquitectura interna de cualquier
ordenador. Finalmente, en 1989 se estableció el estándar ANSI C. Actualmente, cualquier compilador
moderno soporta ANSI C. Sin embargo, probablemente debido a razones comerciales, la opción por
defecto en muchos compiladores de C es una versión propia del desarrollador del compilador. Dicha
version suele ser ligeramente diferente e incompatible con el estándar ANSI C.
El lenguaje C debe su nombre a su predecesor, el lenguaje B desarrollado por Ken Thompson,
también en los Laboratorios Bell.
2.2 Caracterı́sticas del lenguaje
Actualmente existe gran variedad de lenguajes de programación de alto nivel entre los que elegir, como
BASIC, Pascal, C, C++, Java, etc. Todos ellos pueden usarse para resolver la mayoŕıa de proyectos de
programación. Sin embargo, existen algunas razones que hacen de C el preferido de muchos programa-
dores:
� Potencia y flexibilidad. Se ha usado en contextos tan dispares como el desarrollo de sistemas
operativos, procesadores de texto, gráficos, bases de datos, compiladores de otros lenguajes, etc.
2.3. Creación de un programa 8
� Popularidad. Existe una gran variedad de compiladores, libreŕıas, herramientas de apoyo a la
programación, etc. Es el lenguaje predominante en el entorno UNIX.
� Portabilidad. El mismo programa escrito en C puede compilarse y ejecutarse sin prácticamente
ningún cambio en diferentes ordenadores. Esto se debe en gran parte al estándar ANSI C.
� Sencillez. C utiliza pocas palabras clave, por lo que puede aprenderse fácilmente.
� Estructura y modularidad. Los programas en C pueden escribirse agrupando el código en funcio-
nes que a su vez se agrupan en distintos módulos. De esta forma, el código puede reutilizarse.
De acuerdo con esto, C representa una buena elección como lenguaje de programación. Sin em-
bargo, seguro que el lector ha oı́do hablar de los lenguajes C++, Java y de la programación orientada a
objetos, además de preguntarse sobre las diferencias entre C y C++. Pues bien, C++ puede verse como
un superconjunto de C, lo que significa que casi cualquier aspecto de C es perfectamente válido en C++
(pero no al revés). Java por su parte, al igual que C++, también se basa en la sintaxis de C.
Finalmente, diremos que aunque C es considerado como un lenguaje de alto nivel, mantiene muchas
caracterı́sticas de los lenguajes de bajo nivel, por lo que podrı́a clasificarse como de nivel bajo-medio.
2.3 Creación de un programa
La creación de un programa de ordenador consta generalmente de una serie de pasos claramente dife-
renciados.
Edición
El primer paso consiste en usar un editor de textos y crear un fichero que contenga el código del
programa en C. Este código, normalmente llamado código fuente, servirá para dar instrucciones precisas
al ordenador. Por ejemplo, la siguiente linea de código fuente en C indica al ordenador que debe mostrar
el mensaje entre comillas en la pantalla:
printf( "Esto es un mensaje" );
El formato del texto admitido por la mayorı́a de compiladores se basa en el Código Estándar Ameri-
cano para el Intercambio de Información (ASCII). La mayor parte de los procesadores de texto utilizan
códigos especiales para dar formato a los documentos, por lo que normalmente no pueden ser usados
como editores de programas.
Hoy en dı́a, la mayorı́a de los entornos de programación incluyen un editor, sin embargo, otros no.
En estos casos pueden usarse otros programas genéricos de edición de textos ASCII proporcionados por
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 fichero fuente de un programa debe grabarse con un nombre. Normalmente, el nombre del
fichero debe permitir intuir qué hace el programa. Adicionalmente, los ficheros fuente en C suelen
tener la extensión .c para identificarlos fácilmente.
9 2. Primer contacto con C
Compilación
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
la realiza un programa llamado compilador que, dado un fichero fuente, produce un fichero con las
instrucciones de lenguaje máquina correspondientes al programa fuente original. El nuevo fichero
recibe el nombre de fichero objeto.
El fichero objeto suele tener el mismo nombre que el fichero fuente, pero con la extensión .OBJ (o
.o en UNIX).
Montaje
En el tercer paso, las diferentes partes del código compilado se combinan para crear el programa ejecu-
table.
Parte del lenguaje C consiste en una librerı́a de funciones precompiladas que contiene código objeto.
Las funciones en esta librerı́a realizan operaciones de uso frecuente, como mostrar datos en pantalla o
leer datos de un fichero. La función printf del ejemplo anterior es una función de dicha librerı́a. Ası́
pues, el fichero objeto producido al compilar el fichero fuente debe combinarse con el código objeto de
la librerı́apara crear el fichero del programa ejecutable.
Cabe destacar que en la mayorı́a de compiladores actuales, como los que funcionan en MS-DOS o
MS-Windows, compilación y montaje se realizan como si fuesen una sola acción.
2.4 Primeros pasos con C
A continuación se muestra el programa más sencillo posible en C:
void main()
f
g
Todo programa en C debe tener una y sólo una función main(). Esta función deberá constar de una
serie de sentencias (en este caso vacı́a) delimitada por los sı́mbolos f g. Dichas sentencias especifican
la secuencia de acciones que el programa deberá llevar a cabo.
En C pueden ponerse comentarios en cualquier lugar del programa, utilizando los śımbolos /* */.
El compilador de C ignora todo el texto entre el inicio del comentario (/*) y el final del mismo (*/).
Añadir comentarios a un programa en C no incrementa el tamaño de los ficheros objeto ni ejecutable,
ni tampoco ralentiza la ejecución del programa. Veamos un ejemplo de programa con comentarios:
/* Mi primer programa en C */
void main()
f
/* Otro comentario */
g
/* ... y otro comentario */
Sin embargo, no es posible poner un comentario dentro de otro. Por ejemplo seŕıa ilegal:
2.5. El modelo de compilación de C 10
/* Mi primer programa en C */
void main()
f
/* Otro comentario /* Comentario ilegal */ */
g
/* ... y otro comentario */
Pero veamos un programa no tan simple. Por ejemplo, el siguiente programa usa la función
printf, predefinida en la librerı́a estándar stdio.h, para mostrar un mensaje en la pantalla.
/* Mi primer programa en C */
#include <stdio.h>
void main()
f
printf( "Mi primer mensaje en pantalla nn" );
g
2.5 El modelo de compilación de C
A continuación se describe el modelo de compilación de C y los distintos procesos implicados: prepro-
cesador, compilador y montador (ver Fig. 2.1).
Preprocesador
Aunque en el apéndice A se verá en detalle esta parte del proceso de compilación, seguidamente se
describen algunos aspectos básicos.
El preprocesador toma como entrada el código fuente y es el responsable de eliminar los comen-
tarios (ya que en realidad no representan ninguna instrucción) y de interpretar las directivas especiales
del preprocesador, denotadas por el sı́mbolo #. Por el momento destacaremos sólamente dos de las
directivas más utilizadas:
� #include, que incluye un fichero externo dentro del fichero fuente. Se usarán los sı́mbolos
< > para indicar que el fichero se encuentra en un directorio del entorno de compilación, dife-
rente del directorio de trabajo actual. Por el contrario, se usarán los sı́mbolos " " para indicar
fichero locales. Por ejemplo:
– #include <math.h> incluye el fichero con las definiciones de las funciones ma-
temáticas de la librerı́a estándar.
– #include <stdio.h> incluye el fichero con las definiciones de las funciones de
entrada y salida de la librerı́a estándar.
– #include "funciones.h" incluye el fichero funciones.h del directorio actual.
� #define, que define un nombre simbólico. Cuando el preprocesador encuentra un nombre
simbólico en el programa lo substituye por el valor que se le haya asociado con la directiva
#define.
– #define NUM ELEMENTOS 100 define la constante NUM ELEMENTOS con valor 100.
– #define PI 3.1416 define la constante PI.
11 2. Primer contacto con C
Montador
Preprocesador
Compilador
Código fuente
Librerías
Código objeto
Código ejecutable
Figura 2.1: Modelo de compilación de C
Compilador
El compilador de C recibe el código fuente producido por el preprocesador y lo traduce a código objeto
(ficheros con extensión .OBJ en MS-Windows, o extensión .o en UNIX).
Montador
Si un fichero fuente hace referencia a funciones de una librerı́a (como la librerı́a estándar) o a funciones
definidas en otros ficheros fuente, el montador se encarga de:
� combinar todos los ficheros objeto correspondientes,
� verificar que sólo uno de ellos contenga la función principal main() y
� crear el fichero finalmente ejecutable.
2.5. El modelo de compilación de C 12
13 3. Empezando a programar
Capı́tulo 3
Empezando a programar
3.1 Identificadores
Un identificador en un lenguaje de programación es un nombre utilizado para referir un valor constante,
una variable, una estructura de datos compleja, o una función, dentro de un programa. Todo identifica-
dor está formado por una secuencia de letras, números y caracteres de subrayado, con la restricción de
que siempre debe comenzar por una letra o un subrayado y que no puede contener espacios en blanco.
Cada compilador fija un máximo para la longitud de los identificadores, siendo habitual un máximo de
32 caracteres.
C diferencia entre mayúsculas y minúsculas, según lo cual C considerará los identificadorescontador,
Contador y CONTADOR, por ejemplo, como diferentes.
En cualquier caso, nunca pueden utilizarse las palabras reservadas del lenguaje para la construcción
de identificadores. De acuerdo con el estándar ANSI, C consta únicamente de 32 palabras reservadas
(ver Tab. 3.1).
Tabla 3.1: Palabras reservadas de C
auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while
3.2 Estructura de un programa
Todo programa en C consta básicamente de los siguientes elementos:
� Directivas del preprocesador
� Definiciones de tipos de datos
� Declaraciones de funciones
3.3. Variables y constantes 14
Por el momento se tratará únicamente la declaración de la función main() que corresponde al
programa principal. La ejecución de un programa escrito en C siempre comienza por dicha función.
Por esta razón, en un programa sólo puede haber una función con dicho nombre. La definición de tipos
de datos se deja para el capı́tulo 8.
Toda función en C, y en particular la función main(), tiene la siguiente estructura:
tipo datos nombre función ( parámetros )
f
variables locales;
secuencia de sentencias;
g
No entraremos aquı́ en las particularidades de las funciones como el paso de parámetros y la de-
volución de resultados de un tipo de datos determinado (ver Cap. 10). Comentaremos simplemente
que tanto la devolución de resultados como los parámetros son opcionales, y que en la mayoŕıa de
programas sencillos no se usan en la definición del programa principal.
A continuación se muestra, como ejemplo, un programa para evaluar la expresión 3 � 5� 32=4:
/* Evaluando una expresión */
#include <stdio.h>
void main()
f
int a, b, c = 5;
a = 3 * c;
b = 32 / 4;
c = a - b;
printf( "El valor de la expresión es: %dnn", c );
g
El cuerpo del programa principal lo constituyen todas las lı́neas de programa comprendidas entre
los sı́mbolos f y g. En cada una de dichas ĺıneas puede haber una o más sentencias. Una sentencia es
una orden completa para el ordenador. Toda sentencia debe acabar con un punto y coma (;).
3.3 Variables y constantes
Los programas de ordenador utilizan diferentes tipos de datos, por lo que requieren de algún mecanismo
para almacenar el conjunto de valores usado. C ofrece dos posibilidades: variables y constantes. Una
variable es un objeto donde se guarda un valor, el cual puede ser consultado y modificado durante la
ejecución del programa. Por el contrario, una constante tiene un valor fijo que no puede ser modificado.
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 or-
denador con el fin 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 varia-
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 significativo a la variable e indicar el tipo de datos aque
corresponden los valores que almacenará. A continuación se muestra la sintaxis más sencilla de una
sentencia de declaración para una sola variable.
tipo datos nombre variable;
Además, en una sola sentencia pueden declararse varias variables de un mismo tipo de datos, sepa-
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.
tipo datos nombre variable = valor inicial;
3.3.2 Constantes
C admite dos tipos diferentes de constantes: literales y simbólicas.
Constantes literales
Todo valor que aparece directamente en el código fuente cada vez que es necesario para una operación
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
como un valor real de coma flotante. Este tipo de constantes puede escribirse también utilizando alguna
de las notaciones cientı́ficas comúnmente aceptadas (ver Sec. 6.3).
Por el contrario, el resto de constantes numéricas son consideradas por el compilador, como valores
enteros. Pueden usarse tres formatos alternativos:
� Toda constante que comience por un dı́gito distinto de 0 es interpretada como un entero decimal
(esto es, en base 10). Se especifican mediante los dı́gitos del 0 al 9 y el signo positivo o negativo.
� Si una constante comienza con el dı́gito 0, se interpreta como un entero octal (base 8). Se
especifican mediante los dı́gitos del 0 al 7 y el signo positivo o negativo.
� Finalmente, las constantes que comienzan por 0x o 0X se interpretan como enteros en base
hexadecimal (base 16). Se especifican mediante los dı́gitos del 0 al 9, las letras de la A a la F, y
el signo positivo o negativo.
Para saber más sobre los distintos sistemas de numeración, ver el apéndice C.
3.3. Variables y constantes 16
Constantes simbólicas
Una constante simbólica es una constante representada mediante un nombre (sı́mbolo) en el programa.
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 harı́amos con una variable. Una
constante simbólica se declara una sola vez, indicando el nombre y el valor que representa.
Las constantes simbólicas tienen dos ventajas claras respecto a las literales. Supongamos el siguien-
te código para calcular el perı́metro de una circunferencia y el área del cı́rculo que define:
perimetro = 2 * 3.14 * radio;
area = 3.14 * radio * radio;
Si por el contrario se hubiese definido una constante simbólica de nombre PI y valor 3.14, podrı́amos
escribir un código mucho más claro:
perimetro = 2 * PI * radio;
area = PI * radio * radio;
Es más, imaginemos ahora que para incrementar la precisión del cálculo se desea usar un valor más
preciso de la constante �, como 3.14159. En el primer caso debeŕıa substituirse uno a uno el valor
3.14 en todo el programa. En el segundo caso, bastaŕıa cambiar la definición de la constante PI con
el nuevo valor.
El método más habitual para definir constantes en C es la directiva del preprocesador #define.
Por ejemplo, en el caso anterior podrı́amos haber escrito:
#define PI 3.14159
Es decir, el nombre simbólico y a continuación el valor constante que representa.
3.3.3 Entrada y salida de valores
Aunque en el apéndice B se verán con más detalle las funciones de la librerı́a estándar printf y
scanf, se introducen en este punto con el fin de poder realizar algunos programas sencillos.
C utiliza operaciones de entrada y salida con formato. Por ejemplo, la función printf usa como
carácter especial de formato el sı́mbolo de porcentaje (%). El carácter que sigue a este sı́mbolo define el
formato de un valor (constante, variable o expresión). Por ejemplo, %c para valores de tipo carácter o
%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).
char ch;
int num;
. . .
printf( "Esto es un carácter: %cnn", ch );
printf( "Y esto un entero: %dnn", num );
El formato es todo aquello especificado 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
Tabla 3.2: Operadores aritméticos en C
Unarios Signo negativo �
Incremento ++
Decremento ��
Binarios Suma +
Resta �
Multiplicación �
División =
Módulo %
correspondencia entre el formato y la lista de valores, tanto en número como en el tipo de los mismos.
Finalmente, la secuencia especial nn indica un salto de lı́nea.
Por su parte, scanf es una función para la entrada de valores a una estructura de datos, y en
particular a una variable. Su formato es similar al de printf. Por ejemplo:
char ch;
int num;
. . .
scanf( "%c%d", &ch, &num );
Permite introducir desde el teclado un carácter en la variable ch y seguidamente un valor entero en
la variable num. Nótese que en el caso de scanf se antepone el sı́mbolo & a las variables. Por
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 especifica un valor. Los elementos que
constituyen una expresión son: constantes, variables y operadores.
3.4.1 Operador de asignación
El operador de asignación permite asignar valores a las variables. Su sı́mbolo es un signo igual =. Este
operador asigna a la variable que está a la izquierda del operador el valor que está a la derecha. Un
ejemplo de expresiones válidas con el operador de asignación son: x = 1; z = 1.35; .
3.4.2 Operadores aritméticos
Además de los operadores aritméticos tradicionales, C proporciona algunos operadores adicionales (ver
Tab. 3.2).
La expresión, x++; equivale a x = x+1;, y x--; equivale a x = x-1;. Aunque en el
pasado algunos compiladores generaban código más eficiente si se usaba los operadores ++, -- en
lugar de sus expresiones equivalentes, esto ya no es cierto en los compiladores modernos. Los opera-
dores ++ y -- pueden usarse tanto de manera postfija (más habitual) como prefija, indicando en cada
3.4. Expresiones 18
caso si el valor de la variable se modifica después o antes de la evaluación de la expresión en la que
aparece. Por ejemplo, la siguiente lı́nea de código:
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
los dos operandos de la división son enteros, el cociente que se obtiene es el correspondiente a la
división entera (el cociente no se redondea, sino que se trunca). Si alguno de los operandos es un valor
real, el resultado de la división será también real. Por ejemplo, x = 3/2; asigna el valor 1 a la
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
el resto de una división entera, por lo que sus operandos deben ser también enteros. Por ejemplo,
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 ex-
presiones del estilo de i = i + 5; o x = x * (y + 2);. Este tipo de expresiones puede
escribirse en C de forma compacta como:
expresión1 op = expresión2
que es equivalente a:
expresión1 = expresión1 op expresión2
Según esto, la asignación i = i + 5; puede reescribirse como i += 5; y la asignación
x = x * (y + 2); como x *= y + 2;. Nótese que esta última expresión no significa en
ningún caso x = (x *y) + 2;.
Como puede verse, un uso abusivo de los operadores abreviados de C puede hacer dif́ıcil de leer un
programa. C permite escribir expresiones muy compactas, pero que pueden generar confusión al ser
leı́das. Hay que recordar siempre que la legibilidad (mantenimiento) de un programa es tan importante
como su correcto funcionamiento.
3.4.3 Operadores relacionales
Los operadores relacionales se utilizan principalmente para elaborar condiciones en las sentencias con-
dicionales e iterativas (ver Cap. 4 y Cap. 5).
La tabla 3.3 resume los distintos operadores de relación en C. Al relacionar (comparar) dos expre-
siones mediante uno de estos operadores se obtiene un resultado lógico, es decir: ‘CIERTO’ o ‘FALSO’.
Por ejemplo, la expresión 4 > 8 da como resultado el valor falso, la expresión num == num da
como resultado cierto, la expresión 8 <= 4 da como resultado falso, etc.
Es interesante destacar que a diferencia de otros lenguajes, C no dispone de un tipo de datos es-
pecı́fico para los valores lógicos o booleanos. En su lugar, C representa un resultado ‘FALSO’ como
19 3. Empezando a programar
Tabla 3.3: Operadores relacionales (izquierda) y lógicos (derecha) en C
Menor que <
Mayor que >
Menor o igual que <=
Mayor o igual que >=
Igual que ==
Distinto que ! =
Conjunción ó Y lógico &&
Disyunción u O lógico jj
Negación ó NO lógico !
Tabla 3.4: Tabla de verdad de los operadores lógicos en C
A B ! A A && B A jj B
Cierto Cierto Falso Cierto Cierto
Cierto Falso Falso Falso Cierto
Falso Cierto Cierto Falso Cierto
Falso Falso Cierto Falso Falso
el valor numérico entero cero, y un resultado ‘CIERTO’ como cualquier valor entero diferente de cero.
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
= . Por ejemplo, la sentencia x = 3 asigna el valor 3 a la variable x, mientras que x == 3 compara
el valor de x con la constante 3.
3.4.4 Operadores lógicos
Los operadores lógicos (ver Tab. 3.3) se utilizan principalmente en conjunción con los relacionales para
elaborar condiciones complejas en las sentencias condicionales e iterativas (ver Cap. 4 y Cap. 5).
Es importante no confundir los operadores lógicos && y jj con los operadores de manejo de bits
& y j que veremos en la sección 8.5.
La tabla 3.4 muestra la tabla de verdad para los operadores lógicos. De acuerdo con dicha tabla,
las expresiones 4 && 0, !(4 > 1) y 5 <= 0 dan como resultado 0 (falso), mientras que
las expresiones 4 jj 9, (8 == 4*2) && (5 > 2) y 2 && (4 < 9) dan como resultado 1
(cierto).
3.4.5 Prioridad de operadores
La tabla 3.5 muestra los operadores vistos anteriormente, ası́ como la prioridad entre ellos. La prioridad
desciende al descender en la tabla. También se muestra la asociatividad para operadores con el mismo
nivel de prioridad.
Por ejemplo, según dicha tabla, la expresión (a < 10 && 2 * b < c) se interpretará como
(a < 10) && ((2 * b) < c).
3.5. Ejercicios 20
Tabla 3.5: Prioridad y asociatividad de los operadores en C
Operador Sı́mbolo Asociatividad
Paréntesis () Izquierda a derecha
NO lógico ! Derecha a izquierda
Signo negativo �
Incremento ++
Decremento ��
Multiplicación � Izquierda a derecha
División =
Módulo %
Suma + Izquierda a derecha
Resta �
Menor que < Izquierda a derecha
Menor o igual que <=
Mayor que >
Mayor o igual que >=
Igual que == Izquierda a derecha
Distinto que ! =
Y lógico && Izquierda a derecha
O lógico jj Izquierda a derecha
Asignaciones = + = � = 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 área y su perı́metro, y mostrar los resultados
por pantalla.
2. Pedir una cantidad de segundos y mostrar por pantalla a cuántas horas, minutos y segundos
corresponden.
3. Suponiendo que previamente se ha realizado la declaración int x = 7, y; , calcular el
valor de la variable y tras evaluar cada una de las siguientes sentencias de asignación:
(a) y = �2 + ��x;
(b) y + = 2;
(c) y = (y == x);
(d) y = y++ � x;
4. Evaluar las siguientes expresiones:
(a) 5 = 2 + 20 % 6
(b) 4 � 6 = 2 � 15 = 2
21 3. Empezando a programar
(c) 5 � 15 = 2 = (4 � 2)
(d) 8 == 16 jj 7 != 4 && 4 < 1
(e) (4 � 3 < 6 jj 3 > 5 � 2) && 3 + 2 < 12
3.5. Ejercicios 22
23 4. Construcciones condicionales
Capı́tulo 4
Construcciones condicionales
Una de las construcciones importantes que pueden especificarse 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, con-
dicionalmente. Para ello será necesario especificar dichas condiciones (ver Sec. 3.4) y disponer de un
mecanismo para indicar qué acciones tomar dependiendo de cómo se evalúe una determinada condición
en un momento dado de la ejecución del programa.
Antes de empezar, un recordatorio. Como ya de comentó en la sección 3.4.3, C no dispone de
valores booleanos o lógicos, que podrı́an usarse en la evaluación de condiciones. En su defecto, C
“simula” los valores falso y cierto, como el valor numérico cero, y cualquier valor no cero (incluyendo
negativos), respectivamente.
Ası́ pues, en este capı́tulo veremos las distintas maneras que C ofrece para controlar el flujo de
ejecución de un programa de forma condicional, que son:
� la construcción if,
� el operador condicional ?, y
� la construcción switch.
4.1 Construcción if
La construcción if es similar a la existente en otros lenguajes de programación, aunque en C po-
see ciertas peculiaridades. El formato general de esta construcción para decidir si una determinada
sentencia debe ejecutarse o no (alternativa simple) es el siguiente:
if (condición)
sentencia;
La construcción if puede escribirse también de forma más general para controlar la ejecución de un
grupo de sentencias, de la siguiente manera:
4.1. Construcción if 24
Condición
sentencias
Grupo de
Cierto
(a)
Condición
Grupo de Grupo de
sentencias 1 sentencias 2
FalsoCierto
(b)
Figura 4.1: Esquema de funcionamiento de if y de if-else
if (condición)
f
sentencia 1;
sentencia 2;
. . .
sentencia N;
g
El funcionamiento de la construcción if es muy simple. En primer lugar se evalúa la condición,
que no es otra cosa que una expresión de tipo entero. A continuación, si la expresión se ha evaluado
como cierta, se ejecuta la sentencia o grupo de sentencias. En caso contrario la ejecución del programa
continúa por la siguiente sentencia en orden secuencial (ver Fig. 4.1 (a)).
El siguiente ejemplo muestra el uso de la construcción if. El programa lee un número entero y lo
transforma en el impar inmediatamente mayor, si es que no era ya impar.
#include <stdio.h>
void main()
f
int a;
scanf("%d", &a);
if (a % 2 == 0) /* Comprobar si a es par. */
a = a + 1;
printf( "Ahora es impar: %dnn", a );
g
Nótese que después de la condición no se escribe ‘;’. Escribir ‘;’ detrás de la condición equivaldrı́a
a que la construcción if ejectutase un conjunto vacı́o de sentencias, lo cual no tiene ningún sentido.
Nótese, sin embargo, que tal hecho es válido sintácticamente (no produce ningún error de compilación),
por lo que deberá tenerse cuidado al escribir esta construcción. Algo similar ocurre con los bucles
for y while (ver Cap. 5).
25 4. Construcciones condicionales
4.1.1 Variante if-else
Existe otra forma más general, denominada alternativa doble, que ofrece dos alternativas de ejecución,
en función de si la condición se evalúa cierta o falsa.
if (condición)
sentencia 1;
else
sentencia 2;
Y para un grupo de sentencias:
if (condición)
f
grupo de sentencias 1;
g
else
f
grupo de sentencias 2;
g
Ası́ pues, si la condición es cierta se ejecutará la primera sentencia (el primer grupo de sentencias), y si
es falsa se ejecutará la segunda sentencia (el segundo grupo). Ver figura 4.1 (b).
El siguiente programa muestra el usode esta construcción. El programa calcula el máximo de dos
números enteros:
#include <stdio.h>
void main()
f
int a, b, max;
scanf( "%d %d", &a, &b );
if (a > b)
max = a;
else
max = b;
prinf( "El máximo es: %dnn", max );
g
Es importante destacar que la sentencia en la construcción else es opcional, es decir, puede ser
nula. Veámoslo en el siguiente ejemplo que determina si un número es par:
#include <stdio.h>
void main()
f
int x;
4.1. Construcción if 26
scanf( "%d", &x );
if (x % 2 == 0)
printf( "Es par.nn" );
else ;
g
El hecho de que la construcción else sea opcional puede causar problemas de ambigüedad al
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 éste no
tenga ya asociada otra sentencia else.
A continuación se muestran dos porciones de programa prácticamente iguales, pero con comporta-
mientos completamente diferentes. Se deja para el lector el análisis de ambos casos.
. . .
if (n > 0)
if (a > b)
z = a;
else
z = b;
. . .
. . .
if (n > 0)
f
if (a > b)
z = a;
g
else
z = b;
. . .
4.1.2 Variante if-else-if
Existe finalmente otra construcción alternativa muy común, conocida como if-else-if o simple-
mente else-if. Su construcción, donde las condiciones se plantean de forma escalonada, se muestra
a continuación:
if (condición 1)
f
grupo de sentencias 1;
g
else if (condición 2)
f
grupo de sentencias 2;
g
. . .
else if (condición N)
f
grupo de sentencias N;
g
else
f
grupo de sentencias por defecto;
g
27 4. Construcciones condicionales
Las condiciones se evalúan secuencialmente de arriba hacia abajo hasta encontrar una que dé como
resultado cierto. En ese punto, se ejecuta el grupo de sentencias correspondiente a dicha condición. El
resto de condiciones y sentencias asociadas se ignoran. En caso de que ninguna de las condiciones se
evalúe cierta, se ejecutarı́a el grupo de sentencias por defecto. Como en todos los casos anteriores, el
último else es opcional.
A continuación se muestra un ejemplo del uso de esta construcción:
#include <stdio.h>
void main()
f
int hora;
scanf( "%d", &hora );
if ((hora >= 0) && (hora < 12))
printf( "Buenos dı́as" );
else if ((hora >= 12) && (hora < 18))
printf( "Buenas tardes" );
else if ((hora >= 18) && (hora < 24))
printf( "Buenas noches" );
else
printf( "Hora no válida" );
g
4.2 El operador condicional ?
El operador condicional ? es el único operador ternario de C. La forma general de las expresiones
construidas con este operador es la siguiente:
expresión 1 ? expresión 2 : expresión 3;
De manera que si la primera expresión se evalúa cierta, toda la expresión toma el valor de la segunda
expresión. En cambio, si la primera expresión se evalúa falsa, toda la expresión toma el valor de la
tercera expresión.
Un ejemplo tı́pico del uso de este operador es el cálculo del máximo de dos valores. En la siguiente
sentencia, c toma el valor del máximo entre la variable a y b.
c = (a > b) ? a : b;
Esto mismo podrı́a haberse escrito usando la construcción if-else como:
if (a > b)
c = a;
else
c = b;
4.3. Construcción switch 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:
printf("El mı́nimo es %d nn", ((x < y) ? x : y) );
4.3 Construcción switch
Esta construcción permite especificar múltiples sentencias al estilo if-else-if, pero de manera
más compacta, legible y elegante. Su forma general es la siguiente:
switch ( expresión )
f
case constante 1 :
grupo de sentencias 1;
break;
case constante 2 :
grupo de sentencias 2;
break;
. . .
case constante N :
grupo de sentencias N;
break;
default :
grupo de sentencias por defecto;
break;
g
donde la expresión debe ser de tipo entero o carácter, al igual que todas las constantes asociadas a cada
etiqueta case. Es importante resaltar que no pueden usarse variables o expresiones en los distintos
case, sino sólo constantes.
El funcionamiento de la construcción switch es como sigue. En primer lugar se evalúa la
expresión. Seguidamente su valor es comparado secuencialmente con el de las diferentes constantes en
los case. Si el valor de la expresión coincide con alguna de ellas, se ejecuta el grupo de sentencias
correspondiente y switch concluye gracias a la sentencia break. En caso contrario, y si existe el
caso default (que es opcional), se ejecutarı́a el grupo de sentencias por defecto (ver Fig. 4.2).
Cabe mencionar de forma especial, la sentencia break que volveremos a ver en capı́tulos sucesi-
vos. En general, break se utiliza para finalizar de forma forzada la ejecución dentro de un bloque de
código, de manera que la siguiente sentencia a ejecutar será la primera sentencia justo después de dicho
bloque. En la construcción switch, break es necesario para concluir la ejecución del grupo de
sentencias asociado al caso cuya constante coincide con el valor de la expresión. Ası́ pues, la sentencia
a ejecutar después de break en un switch, será la primera sentencia posterior a la llave g que
cierra el switch.
29 4. Construcciones condicionales
Grupo de
sentencias 1
Grupo de
sentencias 2
Expresión
. . . Grupo de
sentencias N
Grupo de
por defecto
sentencias
Figura 4.2: Esquema de funcionamiento de switch
La construcción switch también podrı́a escribirse de forma equivalente mediante sentencias del
tipo if-else-if, de la siguiente forma:
if (expresión == constante 1)
f
grupo de sentencias 1;
g
else if (expresión == constante 2)
f
grupo de sentencias 2;
g . . .
else if (expresión == constante N)
f
grupo de sentencias N;
g
else
f
grupo de sentencias por defecto;
g
que, como puede verse, es mucho más ineficiente en tiempo de ejecución, puesto que la expresión debe
evaluarse repetidas veces, una para cada condición.
El siguiente ejemplo muestra un programa que hace uso de switch para traducir a caracteres un
dı́gito entre 1 y 5.
#include <stdio.h>
void main()
f
int num;
scanf( "%d", &num );
switch ( num )
4.3. Construcción switch 30
f
case 1 :
printf( "Uno.nn" );
break;
case 2 :
printf( "Dos.nn" );
break;
. . .
case 5 :
printf( "Cinco.nn" );
break;
default :
printf( "El dı́gito está fuera de rango.nn" );
break;
g
g
Finalmente, cabe decir que el grupo de sentencias asociado a un case puede ser vacı́o. 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()
f
int num;
scanf( "%d", &num );
switch ( num )
f
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( "Dı́gito no controlado.nn" );
break;
g
g
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 mı́nimo
de ellos.
2. Dado el siguiente programa, realizar un seguimiento de la ejecución en los siguientes supuestos:
(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()
f
int a, b, c, d;
scanf( "%d %d %d %d", &a, &b, &c, &d );
if ( ((a > 0) || (b > a)) && (c != d) )
f
a = c;
b = 0;
g
else
f
c += d;
c = (c == 0) ? (c + b) : (c - a);
b = a + c + d;
g
printf( "%d %d %d %dnn", a, b, c, d );
g
3. Escribir un programa que lea un valor entero y determine si es múltiplo de 2 y de 5.
4. Escribir un programa que muestre por pantalla la ecuación de una recta en un plano, Ax+By+
C = 0, leyendo previamente las coordenadas de dos desus puntos (x1, y1) y (x2, y2). Recordar
que:
A = y2 � y1 y B = y1 � (x2 � x1)� x1 � (y2 � y1)
4.4. Ejercicios 32
33 5. Construcciones iterativas
Capı́tulo 5
Construcciones iterativas
Hasta ahora hemos visto algunos aspectos básicos del control del flujo de ejecución de un programa en
C. Este capı́tulo presenta los mecanismos que C ofrece para la ejecución repetida de sentencias, bien
un número prefijado de veces, bien dependiendo de cierta condición. Es decir, mecanismos para la
creación de bucles de ejecución.
C proporciona las siguientes construcciones iterativas:
� la construcción while,
� la construcción do-while, y
� la construcción for.
5.1 Construcción while
La construcción while es similar a la existente en otros lenguajes de programación. Sin embargo,
debido a que en C toda sentencia puede considerarse como una expresión, la construcción while de
C ofrece cierta potencia añadida.
La forma más general para la ejecución repetida de una sola sentencia es:
while (condición)
sentencia;
O para la ejecución repetida de un grupo de sentencias:
while (condición)
f
grupo de sentencias;
g
El funcionamiento de esta construcción es bastante simple. El cuerpo del bucle, es decir, la sen-
tencia o grupo de sentencias dentro del bucle, se ejecuta mientras el valor de la expresión que actúa de
condición sea cierto. En el momento en que la condición sea falsa, la ejecución del programa continúa
secuencialmente con la siguiente instrucción tras el bucle (ver Fig. 5.1).
5.1. Construcción while 34
Condición
sentencias
Grupo de
Cierto
Falso
Figura 5.1: Esquema de funcionamiento de while
El siguiente ejemplo calcula la media de una secuencia de números enteros leı́dos por teclado aca-
bada en �1:
#include <stdio.h>
void main()
f
int num, cont, suma;
cont = 0;
suma = 0;
scanf( "%d", &num );
while (num != -1)
f
cont++;
suma = suma + num;
scanf( "%d", &num );
g
if (cont != 0)
printf( "La media es %dnn", sum/cont );
else
printf( "La secuencia es vacı́a.nn" );
g
En la construcción while la condición se evalúa al principio del bucle. Por ello, si cuando se al-
canza el bucle por primera vez, la condición es falsa, el cuerpo del bucle no se ejecuta nunca (imagı́nese
el caso, en el ejemplo anterior, en que el primer número de la secuencia sea �1). Como consecuencia,
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 (imagı́nese
qué ocurrirı́a si se elimina la sentencia scanf del cuerpo del bucle del ejemplo anterior). Por ello,
habitualmente las sentencias del cuerpo del bucle modifican las variables que aparecen en la condición,
de forma que ésta sea falsa en algún momento.
Por otra parte, la condición del bucle (y esto es extensible a las diferentes construcciones repetitivas)
35 5. Construcciones iterativas
sentencias
Grupo de
Condición
Falso
Cierto
Figura 5.2: Esquema de funcionamiento de do-while
no tiene por qué ser simplemente una expresión lógica, sino que puede ser cualquier expresión. Por
ejemplo, los siguiente bucles
while (x--) f ... g
while (x = x+1);
son perfectamente válidos. En ambos casos el cuerpo del bucle se repetirá mientras el valor de x sea
distinto de 0. Nótese que en el segundo caso el cuerpo del bucle es nulo, lo cual también es posible.
5.2 Construcción do-while
La forma general de la construcción do-while es la siguiente:
do
f
sentencia; o grupo de sentencias;
g while (condición);
Nótese que tanto para ejecutar una sola sentencia como para ejecutar un grupo de ellas, las llaves
f g son igualmente necesarias.
Esta construcción funciona de manera muy similar a la construcción while. Sin embargo, al
contrario que ésta, do-while ejecuta primero el cuerpo del bucle y después evalúa la condición. Por
lo cual, el cuerpo del bucle se ejecuta como mı́nimo 1 vez (ver Fig. 5.2).
El siguiente ejemplo cuenta el número de veces que aparece el número 3 en una secuencia de
números enteros acabada en �1:
#include <stdio.h>
void main()
f
int num, cont;
5.3. Construcción for 36
Condición
Sentencia inicial
Incremento / Decremento
Grupo de sentencias
Cierto
Falso
Figura 5.3: Esquema de funcionamiento de for
cont = 0;
do
f
scanf( "%d", &num );
if (num == 3)
cont++;
g while (num != -1);
printf( "El 3 ha aparecido %d vecesnn", cont );
g
Es importante destacar el uso de ‘;’ después de la condición, a diferencia de en la construcción
while , donde no se utiliza.
Finalmente, cabe decir que tradicionalmente, tanto la construcción while como la construcción
do-while se utilizan en bucles donde se desconoce a priori el número exacto de iteraciones.
5.3 Construcción for
Esta construcción iterativa no presenta un formato fijo estricto, sino que admite numerosas variantes, lo
que la dota de gran potencia y flexibilidad.
Su forma más general para la ejecución repetida de una sola sentencia es:
for (sentencia inicial ; condición ; incremento/decremento)
sentencia;
o para la ejecución repetida de un grupo de sentencias:
37 5. Construcciones iterativas
for (sentencia inicial ; condición ; incremento/decremento)
f
grupo de sentencias;
g
La primera parte de la construcción for acostumbra a ser una sentencia de asignación donde se
inicializa alguna variable que controla el número de veces que debe ejecutarse el cuerpo del bucle. Esta
sentencia se ejecuta una sola ocasión, antes de entrar por primera vez al cuerpo del bucle.
La segunda parte corresponde a la condición que indica cuándo finaliza el bucle, de la misma forma
que en las construcciones iterativas anteriores. En este caso, la condición se evalúa antes de ejecutar el
cuerpo del bucle, por lo que al igual que en la construcción while, el cuerpo puede ejecutarse entre 0
y N veces, donde N depende de la condición.
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
bucle.
La figura 5.3 muestra esquemáticamente el funcionamiento del bucle for.
El programa del siguiente ejemplo utiliza la construcción for para calcular el sumatorio
10X
i=1
i
3 :
#include <stdio.h>
void main()
f
int i, cubo, suma;
suma = 0;
for (i = 0 ; i <= 10 ; i++)
f
cubo = i * i * i;
suma += cubo;
g printf( "El sumatorio es %dnn", suma );
g
Las tres partes de la construcción for son opcionales, por lo que es posible omitir alguna o todas
ellas. En cualquier caso, los punto y coma (;) separadores son siempre necesarios. Un ejemplo clásico
de este tipo de bucle for es el bucle infinito (nunca concluye la ejecución):
for ( ; 1 ; )
f
/* Grupo de sentencias */
g
Tradicionalmente la construcción for se utiliza en bucles donde el número exacto de iteraciones
es conocido a priori, y puede controlarse mediante una variable que actúa como contador.
5.4. Las sentencias break y continue 38
5.3.1 El operador coma (,)
C permite la utilización de más de una sentencia en la primera y tercera partes de la construcción for,
ası́ como de más de una condición en la segunda parte. Por ejemplo, el siguiente bucle es válido en C:
for (i = 0, j = 10 ; i < 10, j > 0 ; i++, j-=2)
f
/* Grupo de sentencias */
g
Ası́ pues, las variables i y j se inicializan a 0 y 10, respectivamente, antes de comenzar la ejecución
del bucle. En la segunda parte de la construcción, aparecen dos condiciones, i < 10 y j > 0. Si
alguna de ellas es falsa, la ejecución del bucle se detiene. Finalmente, tras ejecutarse el cuerpo del bucle,
i se incrementa en 1 y j se decrementa en 2, tras lo cual vuelven a comprobarse las condiciones, y
ası́ sucesivamente.
5.3.2 Equivalencia for-while
Como se ha podido ver, C trata el bucle for de manera muy similar al bucle while, usando una
condición para decidir cuándo concluye la ejecución. De hecho, todo bucle for puede escribirse

Continuar navegando

Materiales relacionados

560 pag.
curso-de-programacion-cc---fco-javier-ceballos

Escola Municipal Getulio Vargas

User badge image

Alicia Castillo

45 pag.
Programacion Estructurada

SIN SIGLA

User badge image

Vanina Gisele Beguiristain

34 pag.
FortranTutorial

SIN SIGLA

User badge image

nicogomez1214587

33 pag.