Fundamentos de programación. Volumen II. Lenguajes
 9788479084226, 8479084227

Citation preview

Antonio Corbí Bellot / Fernando Llopis Pascual Faraón Llorens Largo / M- José Monllor Domenech Francisco Mora Lizán / Fernando Ortuño Ortín Ernesto Pérez López / Rosana Satorre Cuerda

FUNDAMENTOS DE PROGRAMACIÓN Volumen II. Lenguajes

UNIVERSIDAD DE ALICANTE

FUNDAMENTOS de programación / Antonio Corbí Bellot... [et al.] - Alicante: Universidad de Alicante, 1999 - (Publicaciones de la Universidad de Alicante) (textos docentes) V.II: Lenguajes.— 1999.- 261 p.— Bibliografía.— ISBN 84-7908-422-7 1. Lenguajes de programación. I. Corbí Bellot, Antonio. II. Universidad de Alicante, ed. 519-68 681.3

© 1999 © Universidad de Alicante Publicaciones, 1999

Portada: Gabinete de Diseño Universidad de Alicante Fotocomposición e impresión: Compobell, S.L. Murcia I.S.B.N. (obra completa): 84-7908-420-0 I.S.B.N. (volumen II): 84-7908-422-7 Depósito Legal: MU-1.513-1998 Impreso en España Ninguna parte de esta publicación puede ser reproducida, almacenada o transmitida en manera alguna o por ningún medio, ya sea eléctrico, químico, mecánico, óptico, de grabación o de fotocopia, sin permiso previo del editor.

Quizá una de las cuestiones más debatidas y que más controversias provoque en la enseñanza de la programación, sea la elección del primer o primeros lenguajes de programación. Ello se puede comprobar al observar los diversos lenguajes de programación que se enseñan o incluso a que su estudio no se realice en clases de teoría sino únicamente en las clases prácticas. En base a los objetivos propuestos los lenguajes de programación ideales deberían cumplir las siguientes características: •

Ser un lenguaje de propósito general.



Debe facilitar los hábitos de programación estructurada y programación modular.



Debe posibilitar al usuario la definición de tipos de datos adicionales.



Debe existir un estándar de dicho lenguaje.

Además sería recomendable que estuviera difundido, que existieran versiones de dicho lenguaje para los entornos más utilizados por los alumnos (Linux, MS-DOS, Windows95) y que sus requisitos mínimos de ejecución no fuesen especialmente elevados. También es positivo que dispusiesen de suficiente documentación y bibliografía. Es difícil que un lenguaje cumpla totalmente todos los requisitos, pero si que se puede valorar en conjunto el nivel de cumplimiento de algunos de los lenguajes. De estos cabe destacar dos: •

El lenguaje Pascal: el cual está muy difundido (aunque quizá debido a la versión comercial Turbo Pascal), y es uno de los lenguajes más utilizados en la enseñanza, ya que es un excelente lenguaje para el aprendizaje, al facilitar la adquisición de buenos hábitos para la programación.



El lenguaje C: el cual es un lenguaje flexible y disponible, pero como mayor inconveniente debe indicarse que facilita una programación muy descuidada, que se debe evitar utilizando las técnicas de programación estructurada. Su versión orientada a objetos C++ además ofrece soporte a la noción de tipo abstracto de datos. Una característica a señalar es que C y C++ son los lenguajes más utilizados en las prácticas de asignaturas en cursos posteriores de las titulaciones de Informática en la Universidad de Alicante.

Adicionalmente hay que indicar que a lo largo de la carrera los alumnos deberán estudiar y trabajar con varios lenguajes de programación. Es importante que cuando el alumno se inicie en el estudio de los mismos, pueda empezar a diferenciar que conceptos teóricos son dependientes de un lenguaje de programación y cuales no lo son. Además es positivo que el alumno no se centre únicamente en un lenguaje de programación, ya que el

6 conocimiento de más de un lenguaje facilita el aprendizaje de uno nuevo y además le permite comparar entre diferentes lenguajes y poder valorar cual es el más efectivo para solucionar un problema en concreto. Por todo esto hemos incluido en este volumen un estudio de las principales características de los lenguajes C y Pascal, de tal forma que permita al alumno no sólo un conocimiento de cada uno de ellos por separado sino que además permita comparar las ventajas e inconvenientes de ambos lenguajes. En el libro se realizará un estudio de cómo utilizan ambos lenguajes los conceptos estudiados en el volumen I de esta serie: conceptos básicos de programación (capítulo 7 y 8), vectores y matrices (capítulo 9), registros (capítulo 10), archivos (capítulo 11) y estructuras dinámicas de datos (capítulo 12).

índice Capitalo : 7 Lenguajes C y Pascal. Conceptos Básicos Origen de ambos lenguajes 7.1.1. Orígenes del lenguaje Pascal 7.1.2. Orígenes del lenguaje C 7.2. Identificadores 7.2.1. Pascal 7.2.2. C 7.3. Palabras reservadas 7.3.1. Pascal 7.3.2. C 7.4. Tipos de datos básicos 7.4.1. Pascal 7.4.2. C 7.5. Tipos de datos definidos por el usuario 7.5.1. Pascal 7.5.2. C 7.6. Constantes 7.6.1. Pascal 7.6.2. C 7.7. Variables 7.7.1. Pascal 7.7.2. C 7.8. Expresiones 7.8.1. Pascal 7.8.2. C 7.9. Operadores 7.9.1. Pascal 7.9.2. C 7.10. Sentencias 7.10.1. Pascal 7.10.2. /.ÍU.¿. C V^ .... l.\.

Capitalo

Programación elemental en C y Pascal Program; de un programa 8.1. Estructura Estructurad* 8.1.1. PASCAL

13 13 13 13 14 14 14 14 15 15 16. 16 17 17 17 17 18 18 18 21 21 21 22 22 22 22 22 23 25 25 26 27 27 27

8

índice

8.1.2. C 8.2. Archivos de cabecera 8.3. Entrada y salida de datos 8.3.1. PASCAL, entrada de datos 8.3.2. PASCAL, salida de datos 8.3.3. C, entrada y salida de datos 8.4. Sentencias de control de flujo en Pascal 8.4.1. Selección simple 8.4.2. Selección múltiple 8.4.3. Instrucciones de repetición 8.5. Sentencias de control de flujo en C 8.5.1. Valores verdadero y falso en C 8.5.2. Selección simple 8.5.3. Selección múltiple 8.5.4. Instrucciones de repetición 8.6. Subprogramas 8.6.1. Pascal 8.6.2. C 8.6.3. Paso de parámetros ipiíuío 9. Tipos de datos I. Vectores y matrices Capitalo 9.1. Concepto de vector 9.1.1. Arrays en PASCAL 9.1.2. Arrays en C 9.2. Operaciones con arrays 9.2.1. Acceso a los elementos de un array 9.3. Operaciones con la estructura 9.4. Arrays especiales: Las cadenas de caracteres en Pascal 9.4.1. Operaciones con cadenas en Pascal 9.4.1.1. La cadena vacía o nula 9.4.1.2. Acceso a los elementos de una cadena 9.4.1.3. La función length 9.4.1.4. Operador de Concatenación (+) 9.4.1.5. La función concat 9.4.1.6. copy 9.4.1.7. pos 9.4.1.8. delete 9.4.1.9. insert 9.4.1.10. str 9.4.1.11. val 9.4.2. Tratamiento de cadenas en Pascal Estándar 9.4.2.1. Definición del tipo cadena en Pascal estándar 9.4.2.2. Operaciones con cadenas en Pascal estándar 9.5. Arrays especiales: Las cadenas de caracteres en C 9.5.1. Operaciones con cadenas en C 9.5.2. Funciones de tratamiento de cadenas en C

28 29 30 30 32 34 39 39 40 41 43 43 43 46 47 53 54 59 63 73 73 74 75 76 76 79 82 83 84 84 84 84 84 85 85 85 85 86 86 86 86 87 90 91 92

índice

9.5.2.1. strlenQ 9.5.2.2. strcatQ y strncatQ 9.5.2.3. strcmpQ y strncmpQ 9.5.2.4. strchrQ y strrchrQ 9.5.2.5. strstr() 9.5.2.6. strpbrkQ 9.5.2.7. strtodQ, strtolQ y strtoulQ 9.5.3. Tratamiento de cadenas de caracteres en C estándar 9.5.3.1. Definición e inicialización 9.5.3.2. Operaciones sobre cadenas de caracteres 9.5.4. Funciones de la librería estándar de C de manejo de cadenas 9.6. Búsqueda y ordenación 9.6.1. Métodos de búsqueda 9.6.1.1. Búsqueda secuencial o lineal 9.6.1.2. Búsqueda binaria o dicotómica 9.6.2. Métodos de ordenación 9.6.2.1. Ordenación por intercambio 9.6.2.2. Ordenación por selección 9.6.2.3. Ordenación por inserción 9.7. Ejercicios 9.7.1. Resueltos 9.7.2. Propuestos 9.8 Resumen

Capitalo

Tip< Tipos de datos II. Registros 10.1. Concepto de registro y campo 10.2. Registros en Pascal 10.2.1. Definición de una estructura 10.2.2. Declaración de variables de tipo estructura 10.2.3. Inicialización de estructuras 10.2.4. El array de estructuras 10.2.5. El acceso a los componentes de un registro en Pascal. La sentencia with 10.2.5.1. Registros jerárquicos 10.2.5.2. Registros como operandos y parámetros 10.2.6. Registros variantes 10.3. Registros en C 10.3.1. Definición de una estructura 10.3.2. Declaración de variables de tipo estructura 10.3.3. Inicialización de variables estructura 10.3.4. El array de estructuras 10.3.5. Procesamiento de registros en C 10.3.5.1. Referencia a un campo 10.3.5.2. Asignación 10.3.5.3. Tipos definidos por el usuario: typedef 10.3.5.4. Estructuras y punteros

9 93 93 94 94 94 94 95 95 95 96 96 96 97 97 98 100 100 102 103 104 104 110 111 113 113 114 114 115 115 115 116 117 117 118 119 119 120 121 121 121 121 122 122 123

10

índice

10.3.5.5. Registros como parámetros: Paso de estructuras a funciones 10.3.5.6. Registros variantes: unión 10.4. Ejercicios 10.4.1. Resueltos 10.4.2. Propuestos Capítulo 1 L Tipos de datos III. Ficheros 11.1. Concepto de fichero 11.2. Tipos de ficheros según su función 11.2.1. Ficheros permanentes 11.2.2. Ficheros de movimiento 11.2.3. Ficheros temporales 11.3. Concepto de archivo en Pascal 11.3.1. Archivos secuenciales en Pascal 11.3.1.1. La variable "buffer" del fichero 11.3.1.2. put, get, reset y rewrite 11.3.1.3. El procedimiento PAGE 11.3.1.4. Los archivos INPUT y OUTPUT 11.3.2. Entrada y Salida en Pascal 11.3.2.1. Entrada 11.3.2.2. Salida 11.3.2.3. Programas Interactivos 11.4. Ficheros en C 11.4.1. Ficheros secuenciales en C 11.4.1.1. Reserva del área de buffer 11.4.1.2. Apertura de un fichero 11.4.1.3. Leer/Escribir en un fichero 11.4.1.4. Cerrar el 11.4.2. Ficheros estándar en C 11.4.3. Otras cuestiones 11.5. Ejercicios 11.5.1. Resueltos 11.5.2. Propuestos Tipos de datos IV. Estructuras de datos dinámicas 12.1. Representación estática 12.2. Representación dinámica 12.3. Concepto de puntero 12.4. Punteros en Pascal 12.4.1. Declaración e inicialización 12.4.2. Gestión dinámica de la memoria 12.4.3. Asignación de direcciones y contenidos 12.5. Punteros en C 12.5.1. Declaración e inicialización 12.5.2. Asignación de direcciones y contenidos: operadores de dirección 12.5.3. Punteros y arrays: aritmética de punteros

Capitalo

124 125 126 126 131 133 133 134 134 135 135 135 136 137 137 138 138 139 139 142 143 146 146 146 147 147 14 149 149 150 150 152 153 154 155 156 157 157 158 159 161 161 162 167

índice

12.5.4. Gestión dinámica de la memoria 12.6. Estructuras de datos dinámicas: listas enlazadas 12.6.1. Pilas 12.6.2. Colas 12.6.3. Cola circular 12.6.4. Listas Ordenadas 12.7. 12.6.4.4. Ver el contenido de una lista (verLista): recuperar todos los elementos de la lista, sin eliminarlos, para visualizarlos 12.8. Ejercicios resueltos 12.8.1. Gestión dinámica de cadenas de caracteres 12.8.2. Gestión de pilas y listas ordenadas en Pascal 12.8.3. Gestión de pilas, colas y listas en C

11

170 173 175 178 179 181 186 186 187 191 197

This page intentionally left blank

Capitalo 7

Lenguajes C y Pascal. Conceptos Básicos 7.1.

Origen de ambos lenguajes

7.1.1. Orígenes del lenguaje Pascal El lenguaje PASCAL se creó en 1970-71 por Niklaus Wirth del Techinal Institute of Zurich, el nombre del lenguaje es un homenaje al matemático francés Blaise Pascal, que inventó en 1690 la primera calculadora mecánica de la historia. Es un lenguaje inspirado en el Algol y PL/1 y significa un intento de definir un lenguaje de programación de simple aprendizaje y apto para adaptarse a las especificaciones de los algoritmos y a las definiciones de estructura de datos. Inicialmente se diseñó para ser una herramienta para enseñar los conceptos de programación, pero ha ido adquiriendo gran aceptación y su uso en las empresa, industrias, etc. ha ido creciendo. Es un lenguaje sencillo de aprender, y que facilita su aprendizaje para comprender otros lenguajes no tan fáciles. La Organización Internacional de Estándares (ISO), estableció una descripción oficial del lenguaje de programación Pascal en 1981, que han ido sucesivamente revisando dichos estándares. 7.1.2. Orígenes del lenguaje C El lenguaje C, producto derivado de dos lenguajes precedentes como eran el BCPL y el B, fue desarrollado en los años 70 por Dennis-Ritchie en los laboratorios BELL (AT&T) como consecuencia de la necesidad de una herramienta de programación potente, pero de fácil uso para el diseño del sistema operativo UNIX. Sin embargo, el C, no se dio a conocer al mercado informático hasta que, en 1978, Brian Kernighan y Dennis Ritchie publicaron su libro "The C Programming Language" que presentaba al mundo informático su nuevo lenguaje y sus características más importantes. A partir de este momento la aceptación del C fue rápidamente en aumento hasta convertirse a mediados de los 80, en uno de los lenguajes más importantes y extendidos del mercado. Se escribieron numerosos compiladores e intérpretes de este lenguaje para todo tipo de ordenadores, lo cual contribuyó a favorecer su programación y a aumentar por tanto su popularidad pero provocó, como contrapartida, pequeñas diferencias entre las distintas versiones que fueron

14

Lenguajes C y Pascal. Conceptos Básicos

apareciendo y que se alejaban peligrosamente de los estándares de Richie y Kernighan habían propuesto. Así pues, la expansión incontrolada del C generó ciertas incompatibilidades entre distintas implementaciones del lenguaje y dañó seriamente una de las características más importantes con que contaba el lenguaje: su portabilidad. Como consecuencia, el Instituto Americano de Estándares (ANSÍ), ha propuesto unas normas para que sean adoptadas por todos los compiladores e interpretes de C con lo que, presumiblemente se conseguirán salvar todas las barreras que la expansión incontrolada del lenguaje ha levantado. No obstante el C es un lenguaje que aporta importantes y deseables características, como las que se detallan a continuación: • Se trata de un lenguaje de propósito general y en consecuencia utilizado tanto en la programación de sistemas operativos como en las aplicaciones de los usuarios. • El diseño del lenguaje favorece la programación estructurada y el diseño modular, como se verá durante el curso. • Permite la programación tanto a bajo como a alto nivel. • Consta de un conjunto de instrucciones relativamente pequeño. • Es un lenguaje portable. 7.2.

Identificadores

7.2.1. Pascal • • • •

Formado por letras y números. El primer carácter debe de ser una letra. La longitud máxima depende del compilador que utilicemos (1 carácter mínimo). No hay diferencias entre mayúsculas y minúsculas.

7.2.2. C • • • • 7.3.

Formado por letras, números y el carácter v_'. El primer carácter debe de ser una letra o s _'. La longitud máxima depende del compilador, ANSÍ recomienda 31(1 carácter mínimo). Hay diferencias entre mayúsculas y minúsculas.

Palabras reservadas Es una palabra que tiene un significado especial en un lenguaje y no puede ser utilizada como un identificador. Hay que distinguir entre palabras reservadas, que no pueden ser utilizadas como identificadores, pues tienen un significado propio en el lenguaje e identificadores normalizados, que tienen también un significado en el lenguaje pero pueden ser redefinidos por el programador.

Palabras reservadas

15

7.3.1. Pascal Las palabras reservadas se escriben en mayusculas: AND END MOD REPEAT ARRAY FILE NIL SET BEGIN NOT THEN FOR CASE OF FORWARD TO CONST FUNCTION TYPE OR DIV UNTIL PACKED GOTO IF DO VAR PROCEDURE DOWNTO IN PROGRAM WHILE ELSE LABEL WITH RECORD Los identificadores predefmidos tambien se escriben en mayusculas: Archives : INPUT, OUTPUT. Constantes : FALSE, TRUE, MAXINT. Funciones : ABS, EOLN, PRED, SUCC, ARCTAN, EXP, ROUND, TRUNC, CHR, LN, SIN, COS, ODD, SQR, EOF, ORD, SQRT. Procedimientos : GET, PAGE, READLN, UNPACK, NEW, PUT, RESET, WRITE, PACK, READ, REWRITE, WRITELN. 7.3.2. C Las palabras reservadas (o palabras claves) se escriben en minusculas: auto break case char const continue extern double enum do else Default long int if goto Float for sizeof static return short signed Register union unsigned void switch typedef Struct while Volatile Se podria considerar las funciones de bibliotecas, como un tipo de identificador predefmido que no es aconsejable modificar su significado, writeln, scanf, etc...

16

Lenguajes C y Pascal. Conceptos Básicos

7.4. Tipos de datos básicos El tipo de datos determina cómo se representan los elementos en el ordenador y que tipo de procesos se puede ejecutar sobre ellos.

7.4.1. Pascal A.-Escalares A. 1.-Standard Enteros: INTEGER. Son todos los números positivos o negativos. [+2#2-]n Constan de un signo y dígitos. Puesto que el límite varía de unas máquinas a otras, existe un identificador predefinido, MAXINT, cuyo valor es el mayor que se puede representa. -(MAXINT + 1), es el menor valor que se puede utilizar con este tipo de datos. Reales: REAL. Son números decimales. n.m[E[+l-]n] Cuando se introducen por teclado, hay que poner un dígito por lo menos a cada lado del punto decimal. Hay que tener en cuenta: • No hay que hacer comparaciones de dos números reales para ver si son iguales, es mejor comprobar que su diferencia es menor que un número. • Evitar la resta de dos números reales muy parecidos. • Minimizar el número de cálculos realizados. Lógicos: BOOLEAN. Sólo pueden tomar dos valores TRUE o FALSE. También se les denomina de tipo boolean. Carácter: CHAR. Es el conjunto de símbolos que se puede representar en el ordenador. Se representa entre apostrofes. Cuando se comparan dos caracteres lo que se compara es su representación binaria. A.2.-Definidos por el usuario B.-Estructurados Conjuntos: SET. Matrices : ARRAY. Registros: RECORD. Ficheros : FILE. C.-Punteros : POINTER.

Tipos de datos definidos por el usuario

17

7.4.2. C Entero: int. Representa cantidades enteras. Ocupa dos o cuatro bytes normalmente. Carácter: char. Para representar los caracteres ASCII. Ocupa un byte. Coma flotante: float. para rangos numéricos mayores. Notación exponencial. Ocupan una palabra (cuatro bytes). Coma flotante de doble precisión: double. Es uno de tipo float, pero ocupa dos palabras. Mayor precisión. Vacío: void. Tiene dos usos principalmente: • Declarar explícitamente una función que no va a devolver valor alguno. • Crear punteros genéricos. 7.5. Tipos de datos definidos por el usuario 7.5.1. Pascal Los tipos escalares de usuarios pueden ser definidos de dos formas: • Enumeración: lista de valores que son los que pueden tomar las variables de ese tipo TYPE identificador = (tipol, tipo2, ...); • Subámbito: los elementos son subconjuntos de un tipo escalar predefinido (excepto el real) o un tipo escalar definido anteriormente. Identificador = ValorMinimo.. ValorMaximo; Un valor no puede aparecer en más de un tipo. Los nombres de los valores listados en la declaración de un tipo son constantes de ese tipo. Las asignaciones mezcladas no se permiten. El ordenamiento de los tipos escalares está definido por el orden en el cual ellos son enumerados en la declaración de tipos. El tipo estándard boolean es en sí mismo un tipo escalar definido implícitamente por la declaración: TYPE BOOLEAN =

(FALSE,

TRUE);

Ejemplos: TYPE tcoches = {seat,Audi, BMW, Fiat, Lancis}; TcochesExtranjeros = Audi .. Lancia tlineasHOja=1..61;

7.5.2. C typedef

tipo

nuevoTipo;

Nos permite definir nuevos tipos de datos que sean equivalentes a los tipos existentes. Es muy útil en la definición de estructuras. Ejemplos:

18

Lenguajes C y Pascal. Conceptos Básicos

/*Definir un nuevo tipe de datos de entero*/ tupedef int tentero /* declaracion de variables*/ tentero i j;

/* es equivalente a escribir*/ int i j 7.6. Constantes 7.6.1. Pascal Deben de declararse al comienzo del programa. CONST identificador = literalConstante, Ejemplos:

7.6.2. C Existen tres tipos de constantes: • De carácter: Es todo carácter encerrado entre comillas simples. Otra forma es referenciar el código numérico ASCII al cual corresponde el carácter en octal, de la forma siguiente:" 5#50nn', algunas versiones de C permiten representarlo en hexadecimal v 5#5xnn'. Secuencia de escape: sirven para expresar ciertos caracteres no imprimibles. Comienzan con un x 5#5' y seguida por uno o más caracteres especiales. Se consideran un único carácter. \b \t \n \f \r \" \ ' \\ \0



back space tabulación horizontal nueva linea forrn feed retorno de carro comillas apostrofe backslash nulo

008 009 010 012 013 034 039 092 000

De cadena de caracteres: Una cadena de caracteres, o string, consiste en una serie compuesta por cero o más caracteres encerrados entre comillas dobles. Podemos decir, por tanto que una cadena de caracteres es, en realidad, un array de tipo char donde cada posición del mismo contiene un carácter. Cada cadena de caracteres es finalizada automáticamente con el carácter nulo (5#50), lo que significa que el array que contenga una de estas cadenas tendrá de longitud n+1, siendo V la longitud de la cadena. Se puede utilizar, además, las secuencias de escape de las cadenas de caracteres (p.e: "hola 5#5n").

Constantes

19



Numérica: El lenguaje C permite expresar las constantes en tres sistemas de numeración diferentes: • Notación decimal o en base 10. • Notación octal o en base 8. • Notación hexadecimal o en base 16. Para ello necesitaremos emplear un prefijo de manera que el compilador pueda reconocer la notación que se está utilizando. Así, si un número comienza por O (cero), significará que se está utilizando la notación octal, mientras que si comien-. za por OX ó Ox indicará notación hexadecimal. Además podremos representar los siguientes tipos de constantes numéricas: • Constantes enteras: Su valor es entero, es decir, sin punto decimal, con signo, sin exponente y que se encuentra dentro del rango de valores que el ordenador permite para el tipo entero. • Constantes enteras sin signo: Duplican el rango de valores posibles de las constantes básicas aunque impiden el almacenamiento de números negativos, se le añade la letra U (o u) al final de la misma. • Constantes enteras largas: Pueden tomar valores mayores que las constantes enteras, se le pone la letra L (ó T -/ minúscula-) al final de la misma. • Constantes en coma flotante: Son números que pueden incluir un punto decimal y/o exponente. En caso de que el punto decimal se omita, este se considera implícito a la derecha de la cifra. La forma general de una constante en coma flotante se corresponde con el siguiente esquema, donde los signos positivos pueden omitirse así como el punto decimal o la parte exponencial, pero no ambos a la vez, y además, se puede omitir la parte fraccionaria o la parte entera, pero no ambas. Normalmente las constantes y variables en coma flotante se convierten en C, a cantidades de doble precisión cuando se realizan cálculos con ellas, reduciéndose así el error de redondeo y convirtiéndose de nuevo el resultado al final de los mismos. Este tipo de constante hay que recordar que no son más que aproximaciones. Además, podremos hacer uso de Constantes simbólicas, éstas consisten en un nombre que sustituye una secuencia de caracteres (constantes numéricas, carácter, etc.), p.e.:

#defineo nombre texto Ejemplos: #defineo KINTERES 0.70

#define KPI 3.14

Modificadores. Algunos tipos básicos pueden tener modificadores de tipo: short, long, signed y unsigned. Los modificadores dependerán del compilador que se esté utilizando.

20

Lenguajes C y Pascal. Conceptos Básicos LONGITUD (BITS)

TIPO char unsigned char signed char

8

8 8 16 16 16 8 8 8 32 32 32 32 64 128

int unsigned int signed int short int unsigned short int signed short int long int signed long int unsigned long int float Double long double

RANGO ASCII 0 .. 255 -128 .. 127 -32768 .. 32767 0 .. 65535 -32768 .. 32767 -128 .. 127 0 .. 255 -128 .. 127 -2147483648 .. 2147483649 -2147483648 .. 2147483649 0 .. 4294967296 6 dígitos precisión aprox. 12 dígitos precisión aprox. 24 dígitos precisión aprox.

Conversiones. Es cuando se mezclan variables de un tipo con otro. En la asignación la regla es el valor del lado derecho se convierte al tipo del lado izquierdo. Hay que tener en cuenta: • La conversión de int a float o de float a double no añade ni exactitud ni precisión, sólo cambian la forma de representar un valor. • Algunos compiladores C tratan siempre a una variable char como positiva, independientemente que se convierta a un valor int o float. TIPO DESTINO signed char char char char short int short int

int int float double

TIPO EXPRESIÓN char short int

int long int

in long int long int float double long double

PÉRDIDA INFORMACIÓN destino negativo si valor > 127 signo 8 bits más significativos 24 bits más significativos t 8 bits más significativos 24 bits más significativos 1 6 bits más significativos parte fraccionaria, como mínimo precisión; resultado redondeado precisión; resultado redondeado

Ejemplos: Constantes enteras: Correctas: 12 Incorrectas 1,0 123456789 Constantes enteras octales: Correctas 07 Incorrectas 125 Constantes enteras hexadecimales:

-85 1.0

O 0810

32125 10 5

0125 03.5

095

0777

Variables

21 Correctas 0x15 Incorrectas Oxtg Constantes y modificadores de tipo 909000U 12345679L 1234567890UL Constantes de coma flotante: Correctas 0. 10.65666e23 Incorrectas 1 Constantes de carácter: Correctas á' á' "A" Incorrectas "A" Constantes de cadena de caracteres: acteres: Correctas "pepe" Incorrectas 'pepe' Constantes simbólicas: ttdefine tPEPE "pepe" #define tPI 3.1415

Oxa 0x1.2

OXFFF OfF

12.45

2E-12

-2e+15

1,9 1,9

4e9.8

2e

á1 á' 1 as as'

'\n'

'

" "pej "pepe'

"

10

"\nhola\n"

7.7. Variables 7.7.1. Pascal Se definen después de las constantes. VAR identif icador : tipo; Ejemplos: (* Declaración. * ) VAR x, i, z : INTEGER; casado: BOOLEAN; l e t r a : CHAR;

7.7.2. C Declaración. Una declaración asocia un tipo de datos determinado a un grupo de variables. tipo identificador [ , i d e n t i f i c a d o r ] . . . La declaración finaliza en ; int

radio,

diámetro;

Iniciación. Es cuando a una variable se le da un valor inicial a la vez que se declaran. tipo identificador = constante; Ejemplos: unsigned short int m,n; float x = 3elO, y = -2.10344e-7 char finDeCadena = '\0'; double retención, totalRetenido = 0; char cadena cad[101 = "Hola"

22

Lenguajes C y Pascal. Conceptos Básicos

7.8. Expresiones Una expresión representa una unidad de datos simples. Puede ser: • Una entidad simple: constantes, variables, etc. • Una combinación de entidades simples interconectadas por operandos. El valor de la expresión se convierte automáticamente al tipo del identificado!' de la izquierda. Cuidado con la conversión de tipos. 7.8.1. Pascal Asignación: Variable := expresión Ejemplos: A a x 3

:= := := :=

22; 2 * 3 2 * -3 I DIV 2

(ERROR) Seria correcto x:= 2 * (-3) (ERROR) No se puede asignar una expresión a una

constante

7.8.2. C Asignación: • La forma general es: nombre_variable = expresión

• • •

Es aritmética. No confundir "=' con v :=' (Pascal) v ==' (operador igualdad). Está permitida la asignación múltiple: variablel = variable2 = ... = expresión

• La asignación se efectúa de derecha a izquierda. • Asignaciones particulares: identificador += expresión equivale a identificador = identificador + expresión identificador -= expresión equivale a identificador = identificador - expresión Ejemplos:

7.9. Operadores 7.9.1. Pascal Aritméticos:

23

Operadores

• • • • • •

+ suma. - resta. * multiplicación. / división. DIV división entera, sin parte fracción. MOD división en módulo (resto de división entera).

Relaciónales : , void main(void)

int i,j; printf("\nPon un número:"); scanf("%3d",&i); printf("\nPon otro:"); scanf("%3d",&j); printf("\nlguales"); else if(ilen2 then encontrado:=true

Arrays especiales: Las cadenas de caracteres en Pascal

89

else i:=i+l end {while..do begin}; if encontrado then pos:=i+l else pos:=0 end{funetion pos};

Concatenación de cadenas Otra operación común de cadenas es la concatenación. El resultado de concatenar dos cadenas es una tercera cadena, que contiene los caracteres de la primera cadena, seguidos de los de la segunda. Veámoslo con el siguiente ejemplo: procedure concat sl s2 string var s3 string var lenl len2 len3 i integer begin lenl:=longitud(si); Ien2:=longitud(s2); Ien3:=longitud(s3); if Ien3>strsize then writeln('error - las cadenas son demasiado largas 1 ) else begin s3:=sl; for i:=lenl+l to strsize do s3[i]:=s2[i-lenl] end {else begin} end {procedure concat};

Obtención de una sub-cadena: substring La última operación que vamos a presentar sobre cadenas, es la operación substrmg. substr(sl, i, j, s2) obtiene la cadena s2 con j caracteres empezando en s][l]. Si desde el subíndice / en la cadena si y tomando j caracteres, la cadena si es más pequeña que todo eso, entonces s2 se rellena de blancos. procedure substtr sl string integer var var

begi

90

Tipos de datos I. Vectores y matrices

9.5. Arrays especiales: Las cadenas de caracteres en C Es frecuente emplear vectores para la manipulación de cadenas de caracteres, como nombres de personas, de calles, etc. En estos casos, hemos de declarar la variable con una longitud capaz de alojar el nombre más largo de entre los valores posibles. Así, para almacenar nombres de personas, podemos declarar el vector char nombre[15] si sabemos que 15 es una cota para el tamaño de los nombres que aparecerán durante la ejecución. Ahora bien, resulta muy incómodo tener que rellenar con espacios el resto de las letras hasta la 15" cada vez que leemos un nombre desde el teclado. Para facilitar el tratamiento de estos vectores de caracteres con longitud variable, C tiene un tipo predefinido que resulta muy conveniente: el tipo string (cadena de caracteres). Su declaración es la normal de un vector de caracteres. Lo que cambia es su lectura y escritura. En el momento de leer una cadena de caracteres, el computador los leerá uno a uno hasta encontrar un cambio de línea. Cuando esto suceda, añadirá al final de la secuencia leída un carácter nulo, NULL, representado mediante la secuencia de escape '\0'. (el carácter con código ASCII igual a 0), consiste en una secuencia de escape que se utiliza para indicar el fin de la cadena y no aparece cuando se muestra en pantalla. Esta marca le permitirá saber posteriormente dónde se encuentra el fin de la cadena. Hay que decir que si sabemos que la longitud máxima de las cadenas a tratar es k, hace falta declarar el vector de dimensión k+1 para poder alojar también esta marca. Así, en el ejemplo anterior declararíamos char nombre[16]

Una constante de este tipo se expresa poniendo la cadena de caracteres entre comillas ("). Nótese la diferencia entre una constante de tipo carácter ('a') y su correspondiente constante de cadena de caracteres ("a"), pues la segunda constante está formada por los char 'a' y '$\backslash 0$', no siendo, por tanto, equivalentes ambas. La lectura de una cadena de caracteres se realiza poniendo en el control el formato %s y quitando del nombre de la variable el prefijo \& en la instrucción scanf. Existe otra manera de leer una cadena desde el teclado, usando la instrucción gets con el formato gets(nombre_cadena);

que lee todos los caracteres tecleados hasta pulsar la tecla retorno. Para la escritura, también hace falta utilizar el formato %s en la parte de control del printf. C posee también una gran variedad de funciones para manipular cadenas. Las más comentes son

Arrays especiales: Las cadenas de caracteres en C

91

La primera copia el contenido de cad_2 sobre cad_l. La segunda añade el contenido de cad_2 al final de cad_l. La tercera compara lexicográficamente los contenidos de las cadenas que recibe como entradas y retorna O si son la misma. Si cad_l es mayor que cad_2 entonces retorna un número positivo y, si es menor, uno negativo. Finalmente, strlen(cad) retoma la longitud de cad. Para utilizar estas instrucciones hace falta importarlas del módulo string}. Finalmente, observemos que las cadenas constantes se encierran entre comillas dobles ". Por ejemplo, en char cad[20];. strcpy(cad,"hola"); asociamos la cadena hola a la variable cad. Ejercicio 1. ¿Qué diferencia existe entre n, 'n' y "n"? Mediante la función de biblioteca getcharQ} podemos introducir los caracteres uno a uno dentro del array. De forma análoga, podremos visualizar cada uno de los caracteres del array mediante la función putcharQ}. 9.5.1. Operaciones con cadenas en C Vamos a presentar las funciones de C para implementar algunas operaciones primitivas sobre cadenas de caracteres. Para todas estas funciones, suponemos las uccicua^iuiics declaraciones giuudic^ globales ttdefine

kSTRSIZE

80

char cadena[kSTRSIZE]; La primera función encuentra la longitud actual de una cadena. int

strlen int

(char cadena[

] )

i;

for (i = 0; cadena[i] != '\0'; i + +); return(i); } /*fin de la función strlen */

La segunda función recibe dos cadenas como parámetros. La función retorna un entero que indica la posición inicial de la primera ocurrencia de la segunda cadena de caracteres dentro de la primera cadena de parámetro. Si la segunda cadena no existe dentro de la primera, se retorna -1.

92

Tipos de datos I. Vectores y matrices int strpos

(char sl[], char s2])

int lenl, Ien2; int i, j1, J2; lenl = strlen(sl); Ien2 = strlen(s2} ; for (i=0; i+len2 = á 1 ) and (linea[i]= á 1 ) and (linea[i], únicamente válido en el caso de punteros a estructuras.

Ambas sentencias son equivalentes pero, por razones obvias, la segunda notación está prácticamente abandonada. Recordemos que un miembro de una estructura puede ser de cualquier tipo, incluso un puntero a otro tipo de estructura o al mismo. 10.3.5.5. Registros como parámetros: Paso de estructuras a funciones Distingamos primero entre miembros de estructura y una estructura completa. A la hora de pasar un campo de una estructura como argumento de una función, como variable que es, podemos hacerlo por referencia o por valor.

10.3.5.5.1. Por valor TipoReg v, *w; int miFuncion(int); x = miFuncion(v.edad); y = mifuncion(w->edad);

10.3.5.5.2. Por referencia TipoReg v, *w; int miFuncion(int * ) ; x = miFuncion(&v.edad); y = mifuncion(&w->edad);

Aunque lo mismo se puede aplicar a las estructuras completas, el paso por valor está desaconsejado debido al sistema que utiliza el compilador. El paso por valor, como sabemos, implica la copia de la variable a transmitir en la zona de variables de la función, por lo que la eficiencia en ocupación de memoria y tiempo del procedimiento de apilar y desapilar copias de estructuras completas (que poseen varios campos que pueden ser más o menos complejos) puede resentirse alarmantemente según los casos. Si se pretende optimizar la ejecución del progra-

Registros en C

125

ma lo mejor es optar por el paso por referencia, teniendo cuidado de no modificar el contenido de la estructura.

10.3.5.5.3. Por valor TipoReg v, *w; int miFuncion(TIPOrEG); x = miFuncion(v); y = mi función(*w) ;

10.3.5.5.4. Por referencia TipoReg v, *w; int miFuncion(TipoReg * } ; x = miFuncion(&v); y = mifunción(w);

Aunque antiguamente no se permitía, el estándar ANSÍ reconoce la posibilidad de devolver una estructura completa como resultado de una función mediante la sentencia return(). 10.3.5.6. Registros variantes: unión Mientras que en una estructura cada campo dentro del registro tiene su área de almacenamiento propia, la unión dispone una única zona de memoria para todos sus campos. Esto quiere decir que independientemente del tipo de dato y de su tamaño en bytes, todos los miembros de una unión comparten el mismo espacio por lo que sólo uno de ellos cada vez puede tener un valor. Pensemos por ejemplo en una biblioteca donde podemos encontrar libros y revistas. La base de datos que se quiere diseñar debe contemplar las características especiales de cada objeto. Así, de los libros nos interesa el autor y de las revistas el número del ejemplar. Se utilizará entonces un tipo de estructura que, dependiendo del tipo de publicación, nos suministre uno u otro dato. unión publica { char a u t o r [ 8 0 ] ; int numero;

Aunque se han definido dos miembros para el registro, el espacio reservado en memoria es el de máximo tamaño, en este caso la cadena que va almacenar el nombre del autor. Si se tratara de un libro podríamos acceder a este miembro para conocer su autor, y si fuera una revista accederíamos por el miembro numero. La sintaxis es idéntica a la de una estructura:

126

Tipos de datos II. Registros unión [identificador] { tipo miembrol; tipo miembro2 ; tipo miembroN; }

[listaDeVariables];

La unión únicamente puede almacenar un dato. Es responsabilidad nuestra saber si ese dato se refiere a uno u otro miembro. Se utiliza principalmente para economizar el uso de memoria y donde se necesiten conversiones de tipo. Su procesamiento es exactamente el mismo que en el caso de la estructura. Supongamos una variable v declarada de tipo pública a la que sometemos a la siguiente secuencia de sentencias. strcpy(v.autor,"Vázquez Montalbán"); v.numero = 125;

La ejecución de las sentencias anteriores provocará que el dato almacenado finalmente en la unión v, fuera el entero 125, aunque anteriormente se le asignó a autor un determinado valor. Una forma de mantener controlado en todo momento qué tipo de valor almacena una unión es integrarla dentro de una estructura. struct ejemplares{ chartitulo[128]; int tipo; unión publica datos; char editorial[80]; } registro; Podríamos establecer la convención de que si tipo vale 1 es que se trata de un libro y por lo tanto tendríamos almacenado el nombre del autor, y si es un O que el dato es el número de una revista. if (registro.tipo) puts(registro.datos.autor); else printf("%d\n", registro.datos.numero); 10.4. Ejercicios 10.4.1. Resueltos Ejercicio 1. Leer dos puntos Pl y P2 representados como registros, calcular la longitud del segmento que los une y la pendiente de la recta que pasa por dichos puntos. Realizar dicho programa en Pascal. Definimos un tipo registro con dos campos que representen las coordenadas x e y respectivamente de un punto en el plano.

Ejercicios

127

La entrada se realiza en un procedimiento de lectura que se invoca cada vez que leemos un punto. Tanto la longitud como la pendiente se determinan en funciones que tienen como entrada los dos puntos y devuelven la longitud y la pendiente respectivamente. La ejecución del programa termina cuando los dos puntos son el mismo. programa puntos_plano; uses crt; type Tpunto = record x, y : real; end;

var pl, p2

: Tpunto;

procedure leer_punto (var p : Tpunto); begin write( 'Coordenadas del punto: ' ) ; readln(p.x, p.y) end ; function longitud (pl, p2 : Tpunto) : real; begin longitud := sqrt (sqr(p2.x - pl.x) + sqr(p2.y - pl.y) ) end ; function pendiente (pl, p2 : Tpunto) : real; begin pendiente := (p2.y - pl.y) / (p2.x - pl.x) end ; begin {bloque principal} write ('El proceso acaba iguales ' ) ; leer_punto(pl); leer_punto(p2); repeat writeln('Longitud = writeln; writeln(' Pendiente leer_punto(pl); leer_punto(p2) until (pl.x = p2.x) and end.

cuando los dos puntos son

', longitud(pl,p2) ); =

' , pendiente(pl,p2) );

(pl.y = p2.y)

128

Tipos de datos II. Registros

Ejercicio 2. Se dispone de un tipo de dato registro Tiempo que consta de tres campos: Horas, Minutos y Segundos. Se desea diseñar un procedimiento en Pascal que reciba como entrada la hora inicial desde el comienzo del experimento y el tiempo transcurrido en segundos y que devuelva como salida la hora actual. Se debe suponer un reloj de 24 horas. El tiempo viene expresado en horas (O .. 23), minutos (O .. 59) y segundos (O .. 59). Por lo que el tipo de los campos correspondientes es entero. El procedimiento tiene dos parámetros, un parámetro variable de tipo registro con los campos indicados, otro parámetro transmitido por valor en el cual se pasa el número de segundos transcurridos. Para obtener la hora actual se divide el número de segundos por 3.600 segundos que tiene una hora. El resto se divide por 60 segundos que tiene un minuto. El último resto son segundos que hay que acumular a los segundos iniciales. Los cocientes obtenidos se acumulan al correspondiente campo de horas y minutos. Para obtener el funcionamiento realizamos un programa que lea tiempo inicial y segundos transcurridos. La salida será la hora actual. programa hora_actual uses crt; type

( iriput, output) ;

Ttiempo = record h, mn, sg : integer end; var

hora : Ttiempo; segd : integer;

procedure entrada (var hr : Ttiempo; var seg : integer); begin with hr do repeat write ('Tiempo inicial en horas minutos segundos : ' ) ; readln(h, mn, sg) until (h in [G..23]) and (mn in [0..59]) and (sg in [0..59]); repeat write ('Segundos transcurridos : '); readln(seg) until (seg > 0) end; procedure hora_t (var hr : Ttiempo; seg : integer); var nh, nm : integer; begin

Ejercicios

129 nh := seg div 3600; seg := seg mod 3600; nm : = seg div 60 ; seg : = seg mod 60 ;

{numero de horas}

{acumulamos al tiempo inicial} with hr do begin sg = sg + seg; nm = nm + (sg div 60) ; { por si pasa o iguala 60 . . . } sg = sg mod 60; mn = mn + nm; nh = nh + (mn div 60) ; {por si pasa o igual 60 . . . } mn = mn mod 60; h : = h + nh ; h := h mod 24 {por si pasa o igual 24 . . . } end end; {fin del procedimiento que obtiene tiempo actual} begin {bloque principal} entrada(ñora, segd); write ( 'Tiempo inicial : ' , hora.h, ' hora.sg);

: ' , hora.mn, ' : ' , {transformación

a

tiempo actual} hora_t(hora, segd); {salida de la hora actual} write ( ' Tiempo actual

:

' , hora . h, ' : ' , hora .mn, '

:

hora.sg) end.

Ejercicio 3. Por ejemplo, consideremos una compañía de seguros que ofrece tres tipos de pólizas: vida, automóvil y casa. Un número identifica cada póliza de seguros de cualquier tipo. Para los tres tipo de seguros es necesario tener el nombre del asegurado, dirección, la cantidad asegurada y el pago de la póliza mensual. Para las pólizas de automóvil y casa es necesario además hacer una deducción de una cierta cantidad. Para un seguro de vida, se necesita la fecha de nacimiento del asegurado y beneficiario. Para la póliza de seguro del automóvil, se requieren el número de licencia, el estado, el modelo, y el año. Para la póliza de seguro de la vivienda, se requiere antigüedad de la casa y la presencia de algunas precauciones de seguridad. Definir en C un tipo de estructura de póliza para esta compañía.

1 30

Tipos de datos II. Registros

#define VIDA 1 #define AUTO 2 #define CASA 3 struct dirección { char calle[50]; char ciudadflO]; char provincia[10]; char cp[6];

struct fecha { int dia; int mes; int anyo;

struct póliza { int numpol; char nombre[30] ; struct dirección domicilio; int cantidad; float premio; int tipo; VIDA, AUTO O CASA */ unión { struct { char beneficiario[30]; struct fecha fecnac; } vida; struct { int autodeduc; char licencia[10]; char estado[3]; char modelo[15]; int anyo; } auto; struct { int casadeduc; int anyoconst; }casa; } infopoliza;

/*

Ejercicio 4. Para reducir cualquier fracción de la forma numerador/denominador a sus términos mínimos se utiliza un procedimiento conocido como {\it algoritmo de Euclides}. Este procedimiento es el siguiente: Sea a el más grande entre numerador y denominador y sea b el más pequeño. Divida b entre a, encontrando un cociente q y un residuo r (esto es, a=q, b=r). Establezca a=b y b=r.

Ejercicios

131

Repita los pasos 2 y 3 hasta que b sea 0. Divida el numerador y el denominador entre el valor de a. Escribir una función en C para reducir un número racional.

struct racional { int numerador; int denominador;

void reduce

(struct racional *inrac, struct racional *outrac)

int a, b, rem; if

(inrac->numerador > inrac->denominador) { a = inrac->numerador; b = inrac->denominador; } /* fin del if */ else { a = inrac->denominador ; b = inrac->numerador; } /* fin del else */ while ( b!=0) { rem = a % b; a = b; b = rem; } /* fin de while */ outrac->numerador / = a; outrac->denominador / = a; } /* fin de reduce */

10.4.2. Propuestos

Ejercicio 1. Un array de registros contiene la descripción de personas a efectos estadísticos. Cada registro tiene los campos: nombre, edad, sexo, altura, color de piel, color de ojos, nacionalidad y región. Escribir un programa en Pascal que lea y almacene datos en este array, ordene el array por orden alfabético de nombres y visualice o imprima su contenido. Ejercicio 2. Supongamos que un número real se representa mediante una estructura de C tal como: struct tiporeal { int izq; int der;

132

Tipos de datos II. Registros

en donde izq y der representan los dígitos a la izquierda y a la derecha del punto decimal, respectivamente. Si izq es un entero negativo, el número real representado es negativo. Escribe una rutina para guardar un número real y crea una estructura que lo represente. Escribe una función que acepte tal escritura y retorne el número real representado por ella. Escribe rutinas suma, diferencia y producto que acepten esas dos estructuras y establece el valor de una tercera estructura para representar el número que sea la suma, la diferencia y el producto, respectivamente, de los registros introducidos.

CapitUlo 1

Tipos de datos III. Ficheros

Los programas que hemos estudiado hasta aquí han producido todos alguna salida, y en la mayoría de los casos han aceptado también alguna entrada. Esto lo han hecho por medio de los archivos cuyos nombres son los identificadores estándar input y output. En este capítulo consideraremos éstos y otros archivos en forma más detallada. Los archivos son importantes por tres razones. Primera, un proceso sólo se puede comunicar con su medio ambiente por medio de archivos. Segunda, un proceso es usualmente de vida corta: un programa se carga en la memoria y se ejecuta, y tan pronto como termina la memoria es usada por otro programa. Si el programa no modifica un archivo durante su ejecución, no habrá evidencia de que éste corrió del todo. La tercera razón para la importancia de los archivos es que pueden almacenarse cantidades más grandes de datos en un archivo que en la memoria. 11.1. Concepto de fichero Los ordenadores heredaron los ficheros y el tratamiento de ficheros de los sistemas manuales de tratamiento de la información. Muchas de las ideas y de los términos que se usan derivan de la terminología empleada con los ficheros manuales. Haciendo memoria de las definiciones que aparecían en el capítulo 5, diremos que un fichero es una colección organizada de datos relacionados entre sí; es una colección de registros. El registro, es la unidad elemental que compone los ficheros, es una colección de información homogénea, relativa a un solo objeto. Por ejemplo, un fichero de empleados contiene información acerca de todos los empleados de una empresa, mientras que un registro contendrá información acerca de un solo empleado. En un registro nos referiremos a la información como un todo, y otras veces nos referiremos a una parte de ella, denominada campo. Los campos son las unidades elementales que componen los registros. Pueden ser campos simples o compuestos, estos últimos son los que, a su vez, contienen otros campos, simples o compuestos. Los campos simples son indivisibles.

134

Tipos de datos III. Ficheros

De entre todos los campos que componen el registro, al menos uno de ellos debe tener la función de servir de identificativo del propio registro. El identificativo o clave de un registro es una información que figura en todos los registros del fichero y que permite distinguirlos al no repetirse. Los registros pueden tener varios identificad vos: D.N.I., número de la S.S., código de empleado, etc. Todos estos campos, para ser identificativos deben tener valores irrepetibles. 11.2. Tipos de ficheros según su función Podemos usar criterios de funcionalidad para decidir el tipo de organización más adecuada a la hora de crear un fichero, es decir, según el tipo de información que vaya a contener o los tratamientos en los que vaya a intervenir, los ficheros pueden clasificarse en: permanentes, de movimientos y temporales, sugiriendo normalmente, para cada uno de ellos, una determinada organización. 11.2.1. Ficheros permanentes Contienen información básica para la aplicación y por tanto estos ficheros son conservados permanentemente. Sus registros suelen permanecer invariables durante los procesos en los que intervienen. Parte de sus datos serán perennes y otros podrán cambiar, aunque no es usual que lo hagan frecuentemente. Por ejemplo, un fichero que contenga información de los alumnos tendrá datos inmutables como son el nombre, D.N.I. y fecha de nacimiento del alumno, y tendrá otros datos susceptibles de variar, como el domicilio, curso en el que se encuentra matriculado, etc. A este tipo de fichero se le denomina fichero maestro. Hay otro tipo de ficheros, llamados de situación, en los que hay ciertos campos que se actualizan frecuentemente para reflejar algo que está sucediendo en cada instante, son típicos de los procesos en tiempo real. Por ejemplo, en una aplicación de reserva de vuelos, hay unos datos sobre cada vuelo que permanecen invariables, como son la ciudad de origen y de destino, la hora de salida y llegada (salvo demoras), el tipo de avión, el número de plazas totales, su ubicación y su precio. Sin embargo hay otro dato que debe variar continuamente: la disponibilidad o no de cada plaza en cada instante. Tal vez tengamos la necesidad de guardar información cronológica del estado de estos ficheros, por ejemplo, de los maestros por motivos de seguridad, de los de situación para realizar estudios estadísticos sobre el nivel de ocupación de las plazas en determinadas fechas del año, etc. Esto lo podemos hacer en los denominados ficheros históricos.

Concepto de archivo en Pascal

135

Mientras los dos tipos de ficheros mencionados anteriormente suelen tener una organización indexada, los históricos suelen tener organización secuencial. 11.2.2. Ficheros de movimiento Son utilizados fundamentalmente para realizar tratamientos apoyándose en un fichero maestro, como es el caso del cálculo de la nómina de los trabajadores de una empresa. En este tipo de ficheros tendremos información sobre las incidencias habidas en el mes para cada trabajador: horas extras realizadas, bajas por enfermedad o accidente, absentismo, dietas, gratificaciones, etc. Una vez finalizado el proceso el fichero de movimientos deja de tener utilidad, salvo que posteriormente deseemos repetir el mismo. La organización de estos ficheros puede ser secuencial, otras veces indexada. 11.2.3. Ficheros temporales Son ficheros creados por un proceso para almacenar provisionalmente información de entrada para un segundo proceso, son pues ficheros de trabajo entre procesos, dejando de tener utilidad una vez finalizados los mismos, y su organización suele ser secuencial. 11.3. Concepto de archivo en Pascal En Pascal, un archivo es una variable. Podemos ver que es una clase e variable algo anormal, porque ésta debe existir antes y después que el programa sea ejecutado, y porque éste puede ser más grande que el programa mismo. Por estas razones, las acciones que un programa Pascal puede efectuar con un archivo son restringidas en cierta medida, y el programa puede acceder solamente a una componente de un archivo a la vez. Como los archivos de Pascal son una abstracción de los archivos efectivos, el programa no contiene información sobre la naturaleza física de un archivo. Por ejemplo, aunque se sepa que el efecto de una llamada al procedimiento estándar read será transferir datos de un archivo a una variable en el programa, no se tiene que establecer (ni siquiera saber) de dónde serán obtenidos los datos de unas tarjetas, si de un archivo de disco, o del teclado de una terminal. Es función del sistema operativo asignar archivos efectivos al programa en el tiempo de ejecución. Esto se realiza por medio de instrucciones que no son parte del programa Pascal. La relación entre los archivos formales declarados y referenciados dentro del programa y los archivos efectivos dados por el sistema operativo en el tiempo de ejecución, es aproximadamente análogo a la relación entre los parámetros formales de un procedimiento y los parámetros efectivos dados por el programa que

136

Tipos de datos III. Ficheros

llama al procedimiento cuando éste se ejecuta. Esta analogía se refleja en la sintaxis del encabezado de un programa Pascal la cual es como un encabezado de procedimiento. 11.3.1. Archivos secuenciales en Pascal Un upo file se declara en un programa Pascal escribiendo una descripción apropiada de tipo

const maxcol=80 type colindice=l..maxcol; tarjeta=packed array colindice of char; cardfile=file of tarjeta;

El archivo mismo es declarado como una variable:

var

deck cardfile

El nombre del archivo debe también incluirse en el encabezado del programa: program c a r d s h y f f l e r ( i n p u t ,

output,

deck);

Los archivos estándar input y output no deben ser declarados en la sección de declaración de variables. Sin embargo, input debe aparecer en el encabezado del programa si read, eof o eoln son usados sin un nombre de archivo, y output debe aparecer en el encabezado del programa si write es usado sin un parámetro de nombre de archivo. Algunos sistemas insisten en que output sea mencionado en el encabezado del programa aun si el programa no contiene llamadas para escribir, por lo que hay un destino para mensajes de error. El tipo base del archivo deck es tarjeta. Una componente del archivo es una variable del tipo base. En cualquier momento una componente del archivo es accesible al programa. La componente de deck a la cual tenemos acceso es una variable de tipo tarjeta, y se escribe deck. Es costumbre referirse a una componente de un archivo como un registro del archivo. La palabra Pascal record no causa confusión en este contexto, porque en muchos casos cada registro de un archivo es de hecho una variable record. El archivo se estructura como una secuencia. Podemos representar una secuencia en forma de diagrama como una fila de cajas, como se hace en la siguiente figura: Registro 1

Registro 2

Registro 3

Registro n

Registro m

Concepto de archivo en Pascal

137

11.3.1.1. La variable "buffer" del fichero Pascal estándar suministra una variable especial, denominada variable ""buffer" de archivo y que se representa con f\, donde f es el nombre de la variable archivo, que es una variable del mismo tipo que las componentes del archivo. Esta variable no tiene que ser declarada y se puede considerar variable como una especie de ventana a través de la cual podemos acceder, tanto en lectura como en escritura, aunque no de forma simultánea, a los componentes del archivo (registros).

var

f : file of integer

Esta declaración implica la existencia de la variable / que almacena números enteros. 11.3.1.2. put, get, reset y rewrite Para operar con la variable buffer del archivo, Pascal estándar proporciona dos procedimientos especiales put y get que permiten escribir el contenido de la variable f\ y moverla durante el procesamiento de archivos. Estas variables también están relacionadas con los procedimientos de apertura de archivos reset y rewrite. Los procedimientos que utiliza Pascal estándar para el procesamiento de archivos secuenciales y su relación con la variable buffer de archivo/son los siguientes: •

reset(f) Posiciona la variable ¿sobre el primer elemento del archivo. Si el archivo está vacío, el contenido de la variable / es indefinido y eofíf) toma el valor true. Si el archivo contiene elementos, el contenido de/es el del primer registro del archivo y eofíf) toma el valor false.



rewrite(f) Operación que reescribe el archivo, de modo que si existiera previamente, se perderían los datos, es decir, sea cual sea el contenido de / lo sustituye por el archivo vacío, eofíf) toma el valor true y se pueden grabar registros en el archivo.



get(f) Hace avanzar la ventana del archivo / al próximo componente del mismo, es decir, asigna a la variable buffer de archivo el contenido del siguiente registro al registro actual. De no existir tal componente el valor de/es indefinido y eofíf) toma el valor true. Por tanto, el efecto de geff/) sólo está definido si todavía existen registros en el archivo.



put(f) Permite añadir el contenido de la variable / al archivo abierto para escritura. Sólo se puede utilizar si eofíf) es true, es decir, al final del archivo correspondiente.



Relación de get(f) y put(f) con read y write

138

Tipos de datos I I I . Ficheros

Los procedimientos read y write están íntimamente relacionados con las variables registro que reciben los valores correspondientes. Las relaciones son las que se indican a continuación: re ad(f, registro) equivale a registro: =f;

get(f)

es decir, cuando utilizamos la sentencia read, implícitamente estamos manejando la variable buffer de archivo, cuyo contenido pasa a la variable registro correspondiente y avanzando la variable buffer de archivo al siguiente componente del mismo mediante el procedimiento get. \vrite(f,registro) equivale/:= registro; put(archivo) el procedimiento write asigna a la variable buffer de archivo el contenido de la variable registro y a continuación lo escribe, mediante el procedimiento put, en el archivo correspondiente. El empleo de los procedimientos read y write es más sencillo y breve que el empleo de sus equivalentes f, put y get, ya que gracias a ellos nos evitamos considerar el valor de la variable /, indefinido en algunas ocasiones, si bien, el empleo de la variable / se puede emplear como lectura anticipada de los registros del archivo. 11.3.1.3. El procedimiento PAGE Pascal estándar proporciona este procedimiento especial, cuando el contenido de un archivo se lista por impresora y se quiere obtener un salto de página. 11.3.1.4. Los archivos INPUT y OUTPUT Pascal estándar define dos archivos de texto (TEXT) por defecto que son los archivos input y output, que se corresponden con los periféricos estándar de entrada y salida en la instalación, normalmente teclado y pantalla o impresora. Los procedimientos read y write cuando omiten el archivo empleado se sobrentiende que leen y escriben a los dispositivos de entrada/salida estándar, por lo que input se asocia al teclado y output a la pantalla, por ello es obligatorio su utilización como parámetros en el encabezamiento del programa. Es importante recordar que los archivos de texto se dividen en líneas formadas por conjuntos de caracteres de impresión y separadas unas de otras por caracteres de control especiales. En el código ASCII la marca separatoria de líneas está constituida por la combinación de caracteres CR/LF (retorno de carro/alimentación o avance de línea). Quiere decirse con esto que al ser caracteres separadores de líneas, no deben asignarse a variables de tipo carácter (char), pudiendo generarse y detectarse con los procedimientos específicos de lectura y escritura para archivos textos readlen y writeIn.

Concepto de archivo en Pascal

139

En este tipo de archivos también se utiliza la variable booleana Eoln (f) para indicar si j\ se encuentra o no sobre la marca separadora de líneas. Eoln (f) toma el valor true si/se encuentra sobre la marca de fin de línea o de final de archivo. En algunas implementaciones de Pascal el contenido de/cuando se encuentra en la marca fin de línea es un carácter en blanco. 11.3.2. Entrada y Salida en Pascal Las convenciones usadas para la entrada y salida deben seleccionarse de acuerdo con el modo en el cual habrá de ejecutarse el programa. En general, los programas ideados para ser ejecutados en forma de lote deben producir más salida que aquellos pensados para ser ejecutados en forma interactiva. En particular, un programa en lote debe copiar todo o la mayor parte de su archivo de entrada en el archivo de salida, junto con anotaciones adecuadas. Esto es lo que el compilador Pascal efectúa, por ejemplo. El archivo de entrada para el compilador Pascal es su programa Pascal, y su salida es también su programa Pascal, junto con los encabezados, números de línea y tal vez mensajes de error. El compilador Pascal también genera otro archivo que contiene la traducción al lenguaje de máquina de su programa. El listado de programa es una parte importante de la salida del compilador; sin él, se debería obtener un listado del programa utilizando un servicio de copia por separado, y sería más difícil relacionar los mensajes de errores con el programa. Consideraciones similares se aplican a otros programas; si el programa imprime solamente respuestas, no podemos encontrar seis meses después preguntándonos cuáles fueron las preguntas. 11.3.2.1. Entrada Un programa que no lee datos es inútil porque hará exactamente las misma cosas cada vez que se ejecute. Algunas veces es atrayente, especialmente si se está utilizando un sistema con buenos servicios de edición interactivas, para incorporar los datos dentro del programa mismo en la forma de declaraciones de constantes, y después alterar estas declaraciones antes de cada ejecución. Esto es una mal práctica de programación. Las declaraciones de constantes están destinadas a ser usadas para definir valores que se utilizan en muchas ejecuciones diferentes del programa. Cualquier valor que es cambiado cada vez que se ejecute el programa, es dato de entrada y pertenece al archivo de entrada. Los datos de entrada pueden estar en formato libre o formato fijo. Pascal es más conveniente para leer datos en formato libre, en virtud de su capacidad de leer un carácter a la vez en el archivo de entrada. Cuando estemos diseñando un programa para leer datos en formato libre, encontraremos útil dibujar diagramas de sintaxis para las estructuras de datos de entrada permitidas. Esto simplificará el diseño del programa y también ayudará a otros usuarios cuando estén preparados para éste. La entrada en formato fijo es el método más viejo, data del tiempo cuando se usaban con profusión los paquetes de tarjetas en el procesamiento de datos. La

140

Tipos de datos III. Ficheros

tarjeta (o registro de entrada) está dividida en campos de longitud fija, y a cada objeto de los datos se le asigna un campo. Los registros de campo fijo en Pascal se manejan con máxima facilidad leyendo el registro entero en un array y después validando y con virtiendo cada campo a su vez. Supongamos que un programa leerá fichas de 80 columnas con varios campos numéricos sin signo, y que hemos declarado: const maxcol=80; type colindice=l..maxcol; imagentarj=packed array[colindice] of char; Vd-L

data:file of imagentarj; inputtarj:imagentarj ; Entonces podríamos utilizar el siguiente validar y convertir campos seleccionados:

procedimiento

para

procedure convertecamponumerico(tarj:imagentarj; primero,ultimo:colindice; var valor:integer; var error¡boolean); const blanco=' base=10;

' ;

var col:colindice; begin valor:=0; error:=false; col:=primero; while (tarj[col]=blanco) and (col FILE *f; f = fopen("texto.txt", "r"); while (!feof(f)) putchar(fgetc(f)) ;

En stdio.h está definida como EOF (End Of File) y debemos comprobar, sobre todo en las lecturas secuenciales, si hemos llegado a esa marca o no. Para ello disponemos de una función específica que devuelve kCIERTO si estamos sobre el final del fichero y kFALSO en caso contrario: int feof {variablefichero}

En el ejemplo abrimos el fichero TEXTO. TXT en modo lectura y mientras no encontremos el EOF leemos un carácter del fichero y lo imprimimos por la pantalla. Otra marca habitual en los ficheros de texto es el fin de línea, EOLN (End Of LiNe), que podemos escribir en el fichero, por ejemplo, con fputc(\i',f). Sin embargo, y en el caso particular del MS-DOS, el EOLN de un archivo en disco es una pareja de caracteres: (Carriage Return(13)+Line Feed(lO)). El compilador efectúa automáticamente la traducción al escribir sobre el fichero, pero a la hora de leer del archivo debemos tener en cuenta este hecho.

Ficheros en C

149

11.4.1.4. Cerrar el fichero Una vez hemos realizado todas las operaciones de mantenimiento del archivo, la última operación que nos garantiza que todos nuestros datos se almacenarán correctamente en el dispositivo externo es el cierre del fichero. int

fcióse(variableFichero)

La terminación del programa cierra todos los ficheros abiertos, pero no es aconsejable ni una buena práctica de programación el dejar esta responsabilidad en manos del compilador. También debemos cerrar un fichero antes de abrirlo de nuevo si lo que queremos es cambiar el modo de apertura con el que fue abierto en principio. 11.4.2. Ficheros estándar en C En C (y en la mayoría de lenguajes de programación) los dispositivos de entrada/ salida son tratados como ficheros de texto. Son archivos predefinidos que no necesitan ser abiertos por el programador y con los que podemos trabajar de igual forma que si fueran archivos normales. Las variables de tipo fichero definidas para cada uno y a qué dispositivo se refieren se muestran a continuación: stdin stdout stderr stdprn

standard input standard output standard error standard printer

teclado pantalla pantalla impresora

Por ejemplo, el escribir en pantalla se puede escribir de diferentes maneras, según utilicemos las funciones específicas de manejo de archivo o no: fgetc(stdin) => fputc(c, stdout) =>

getcharQ putcharQ

También podríamos desear desviar los datos que debían aparecer en la pantalla hacia un impresora conectada al puerto paralelo de nuestro ordenador: fputc(c, stdprn); fprintf(stdprn, formato, listaVariables);

11.4.3. Otras cuestiones En general, a la hora de trabajar con ficheros se nos pueden presentar muchas situaciones de error, como puedan ser que el disco ya no tenga espacio libre para almacenar más datos, que el sistema operativo no haya encontrado el fichero que queremos abrir, que los discos flexibles estén protegidos contra escritura, etc. Es

150

Tipos de datos III. Ficheros

responsabilidad nuestra tener siempre controlado este hecho, sobre todo a la hora de abrir el fichero: if

( (f = fopen("TEXTO.TXT" , " r " ) ) == NULL) rutinaError();

También: if

(ferror ( f)) rutinaError();

Los ficheros que hemos tratado aquí se almacenan en forma de caracteres ASCII por lo que son perfectamente legibles mediante un procesador de textos o un editor. De hecho podríamos crear un fichero mediante un programa de este tipo y leerlo después en nuestro programa C, o bien por cualquier otro medio siempre y cuando fuera un archivo de texto. 11.5. Ejercicios 11.5.1. Resueltos Ejercicio 1. Una empresa quiere obtener un fichero de las existencias que estén por debajo de un cierto umbral leído de una ficha. Escribir un programa en Pascal que extraiga del fichero de existencias aquellos elementos indicados, listando simultáneamente el valor calculado de los mismos. program extraer (vexis, const longnom = 12; longdesc = 25;

nexis, input, output);

type item = record nombre : packed array[1..longnom] of char; descripción : packed array [1..longdesc] of char; coste : real ; (* unitario *) existencia : integer end;

var vexis, nexis : file of item; elemento : item; conta, umbral : integer; begin reset(vexis); (* para comenzar lectura *) rewrite(nexis); (* para comenzar escritura *) writeln('nombre descripción valor exist '); writeln ( ' ') ;

Ejercicios .

151 writeln; read(umbral); conta:=0 ; while not eof (vexis) do begin rdato = elemento; ptL=*inicio; while (píL'-NULL píA=ptL:

&& ptL->dato < elemento) {

184

Tipos de datos IV. Estructuras de datos dinámicas

ptL=ptL->xig; if(ptL!=NULL)

&& (ptL->dato==elemenfo) { puts ("elemento duplicado"); free(ptN);

else { píN->sig=ptL; if (ptL= = * inicio) *inicio=ptN; else ptA->sig=ptN;

return;

El problema que presenta este tipo de expresiones es que, si el compilador no tiene dicha modalidad de evaluación en cortocircuito, se provocará un error de ejecución cuando se llegue al caso de que ptL sea igual a NULL, pues entonces ptL->dato provocará un error, ya que el acceso a ptL->dato estará indefinido, pues esa zona de memoria no pertenecerá a ningún elemento de la lista.

4 Supresión de un elemento de la lista ordenada (borrarDeLista): acción que consiste en buscar un elemento de la lista y eliminarlo de la misma, actualizando el puntero del elemento anterior para que apunte al elemento siguiente del que queremos eliminar. Procedure borrarDeLista (var inicio :Tenlace; elemento: Telemento); var ptA, ptL:Tenlace; mayorOigual: boolean; begin ptL: = inicio; mayorOigual:=FALSE; while not (ptL =nil) and not mayorOigual do if ptL*.dato >= elemento then mayorOigual: = 77?UE else begin ptA:=ptL; ptL:=ptLA.sig end; if mayorOigual and (ptLK.dato=elemento) then begin

Estructura de datos dinámicas: listas enlazadas

185

if (ptL=inicio) then inicio: =ptL*.sig else ptA^. sig: =ptLA.sig; dispose(ptL) end else \vriteln ('no existe el elemento en la lista') end; voíd borrarDeLista (Tenlace *inicio, Telemento elemento) Tenlace ptA, ptL; int mayorOigual; ptL=* inicio; mayorOigual=kFALSE; while (ptL!=NULL && !mayorOigual) if(ptL->dato >= elemento) mayorOigual=kTR UE; else { ptA=ptL; ptL=ptL->sig; if (mayorOigual) if (ptL- >dato==elemento) { if (ptL= = *inicio) * inicio =ptL->sig; else ptA->sig=ptL>sig; free(ptL); return;

Veamos la versión en la que evaluamos las expresiones booleanas en cortocircuito, nótese la claridad y concisión respecto al algoritmo anterior. void borrarDeLista (Tenlace *inicio, Telemento elemento) Tenlace ptA, ptL: ptL=*inic¡o; while (ptL'=NULL && ptL->dato < elemento) { ptA =ptL; ptL=ptL->sig; if(ptL!=NULL) && (ptL->dato==elemento) { if{ptL==* inicio) *inicio=ptL->sig; else ptA->sig =ptL->slg;

186

Tipos de datos IV. Estructuras de datos dinámicas free(ptL); else puts ("no existe el elemento en la lista") return;

12.7. 12.6.4.4 Ver el contenido de una lista (verLista): recuperar todos los elementos de la lista, sin eliminarlos, para visualizarlos Procedure verLista (inicio :Tenlace); begin while inicioonil do begin writeln (inicio^.dato); inicio: = inicio^, sig end end; void verLista (Tenlace inicio) while (inicio!=NULL) { printf ("%c", inicio>->dato); inicio=inicio->sig; return; 12.8. Ejercicios resueltos Veamos una serie de ejercicios, de gestión dinámica de la memoria, que nos ayuden a comprender mejor los conceptos explicados en el capítulo. Para ello, veremos un ejemplo del tratamiento de cadenas de caracteres y luego, en otros ejemplos, haremos uso de las diferentes estructuras de datos dinámicas (pilas, colas y listas) explicadas en este capítulo. 12.8.1. Gestión dinámica de cadenas de caracteres Se trata de leer una cadena de texto, introducida por teclado, y almacenarla dinámicamente; una vez hecho esto, analizaremos la misma para determinar el número de veces que aparece cada palabra en la cadena. Al finalizar el programa, se visualizará la frecuencia de aparición de cada una de las palabras y una cadena formada por la concatenación de las diferentes palabras halladas.

Ejercicios resueltos

187

TRATAMIENTO DE CADENAS UTILIZANDO PUNTEROS ^^^>j;;Jí^:^í{í^>ií;j;;fc^^;íí:fc>jí^:^ífc^^ílí^>¡í>J;íj;^%;i 3»?S 1 K-f VI I Cll IC1 S

[Abelson85]

Structure and Interpretation of Computer Programe. Abelson, H. y Sussman, G. J. The MIT Press / McGraw-Hill Book Company, 1985.

[Aho74]

The Design and Analysis of Computer Algorithms. Aho, A., Hopcroft, J. y Ullman, J. Addison-Wesley, 1974.

[Aho83]

Data Structures & Algorithms. Aho, A., Hopcroft, J. y Ullman, J. Addison-Wesley, 1983. Versión traducida: Estructuras de Datos y Algoritmos. Addison-Wesley Iberoamericana, 1988.

[Alonso88]

Técnicas de Programación. Alonso Amo, F. y Morales Lozano, A. Paraninfo, Madrid, 1988.

[Alonso94]

Metodología de la Programación: Programación Estructurada. 2edición. N\- Dolores Alonso, Silvia Rumeu. Ed. Paraninfo, 1994.

[Antonakos97] Programación estructurada en C. James L. Antonakos, Kenneth C. Mansfield Jr. Prentice Hall, 1997. [Balcazar93]

Programación Metódica. Balcázar, J. L. McGraw-Hill, 1993.

[Biond¡88]

Introducción a la Programación. Tomos I, II y III. Biondi, J., Clavel, G. y Jorgensen, F.B. Editorial Masson, 1988.

[Brassard90]

Algorítmica: Concepción y Análisis. Brassard, G. y Bratley, P. Ed torial Masson, 1990.

[Castro94]

Curso de Programación. Castro, J., Cucker, F., Messeguer, X., Rubio, A., Solano, L. y Valles B. McGraw-Hill, 1994.

[Collado87]

Estructuras de Datos. Realización en Pascal. Collado, M., Morales, R. y Moreno, J.J. Ediciones Díaz de Santos, S.A., 1987.

[Corman91]

Introduction to Algorithms. Gorman, T.H., Leisson, C.E. y Rivest, R.L MIT Press, 1991.

[Crawley90]

PASCAL. Programación Estructurada. Crawley, J. Winston y McArthur, William G. Prentice-Hall Hispanoamericana, 1990.

210

Bibliografia

[Dahl72]

Structured Programming. Dahl, D.J., Dijkstra, E.W. y Hoare, C.A.R. Academic Press, 1972.

[Dale89]

Pascal y Estructuras de Datos. Dale, Nell y Lilly, Susan C. McGrawHill, Madrid, 1989.

[Deitel94]

Como programar en C/C++. H. M. Deitel and P. J. Deitel. PrenticeHall, 1995.

[Dijkstra76]

A Discipline of Programming. Dijkstra, Edsger W. Prentice-Hall, Englewood Cliff, N.J. 1976.

[Dijkstra88]

A Method of Programming. Dijkstra, Edsger W. y Feijen, W.H.J. Addison-Wesley, 1988.

[Esakov89]

Data Structures: an advanced approach using C. Esakov, J. y Weiss, T. Editorial Prentice Hall, 1989.

[Fernandez92] Algoritmos. Problemas resueltos y comentados. Fernandez, J., Oliver, F.J. y Sanchez, J.L. Editorial Paraninfo, Madrid, 1992. [Field88]

Functional Programming. Field, A.J. y Harrison, P.G. International Computer Science Series. Addison-Wesley Publishing Company, 1988.

[Findlay84]

Pascal: Programacion Metodica. Findlay, W. y Watt, B. A. Editorial Rueda, 1984.

[Gries76]

The Science of Programming. Gries, D. Editorial Prentice Hall, 1976.

[Grogono84]

Programacion en Pascal. Grogono, Peter. Ed. Fondo Educative Iberoamericano, 1984. Edicion revisada: Addison-Wesley, 1996.

[Gottfried93]

Programacion en C. Gottfried, Byron S. McGraw-Hill, 1993.

[Horowitz78]

Fundamentals of Computer Algorithms. Horowitz, Ellis y Sahni, S. Computer Science Press, 1978.

[Horowitz84]

Fundamentals of Programming Languages. Horowitz, Ellis. SpringerVerlag, 1984.

[Horowitz90]

Fundamentals of Data Structures Using PASCAL Horowitz, Ellis y Sahni, S. Computer Science Press, 3§ ed., 1990.

[Jackson75]

Principles of Program Design. Jackson, M. A. Academic Press, 1975.

[Jensen78]

Pascal. User manual and report. Jensen, K. y Wirth, N. Springer Verlag, 1978.

Bibliografía

211

[Joyanes95]

Pascal y Turbo Pascal. Un enfoque práctico. Joyanes, L, Hermoso, A. y Zahonero, I. McGraw-Hill, 1995.

[Joyanes96a]

Fundamentos de Programación. Algoritmos y estructuras de datos. Segunda edición. Luis Joyanes Aguilar. McGraw-Hill, 1996.

[Joyanes96b]

Fundamentos de Programación. Libro de problemas. L. Joyanes, L. Rodríguez, M. Fernández. McGraw-Hill, 1996.

[Joyanes96c]

Fundamentos de Programación. Libro de problemas en Pascal y Turbo Pascal. L. Joyanes, L. Rodríguez, M. Fernández. McGrawHill, 1996.

[Kaldewaij90]

Programming, the derivation of algorithms. Kaldewaij, A. Prentice Hall, 1990.

[Keller83]

Programación en Pascal. Keller, A. M. McGraw-Hill, 1983.

[Kernighan91]

El Lenguaje de Programación C. Kernighan, Brian W. y Ritchie, Dennis M. Prentice-Hall Hispanoamericana, S. A., Segunda edición, 1991.

[Kingston90]

Algorithms and Data Structures. Kingston, J. H. Addison-Wesley Publishing Company, 1990.

[Knuth86]

El Arte de Programar Ordenadores. Volumen I: Algoritmos Fundamentales. Volumen II: Algoritmos Seminuméricos. Volumen III: Ordenación y Búsqueda. Knuth, D. E. Reverte, 1986.

[Liskov86]

Abstraction and Specification in Program Development. Liskov, Barbara y Guttlag, John. The MIT Press / McGraw-Hill Book Company, 1986.

[Lipschutz89]

Estructuras de Datos. Lipschutz, Seymour. McGraw-Hill, Madrid, 1989.

[López98]

Fundamentos de Programación. José López Herranz, Enrique Quero Catalinas. Ed. Paraninfo, 1997.

[Manber89]

Algorithms: A Creative Approach. Manber, U. Addison-Wesley, 1989.

[Martin86]

Data Types and Data Structures. Martin, J. J. Prentice-Hall, 1986.

[Peña97]

Diseño de Programas. Formalismo y Abstracción. 2- edición. Ricardo Peña Mari. Prentice Hall Hispanoamericana, S.A., 1997.

[Pratt96]

Programming Languages: Design and Implementation. Pratt, Terrence W. y Zelkowitz, Marvin V. Prentice-Hall International Inc. Third Edition 1996.

212

Bibliograffa

[Quero97]

Programacion en lenguajes estructurados. Enrique Quero Catalinas, Jose Lopez Herranz. Ed. Paraninfo, 1997.

[Sanchis92]

Programacion con el lenguaje Pascal. Sanchis, F.J. y Morales, A. Editorial Paraninfo, Madrid, 1992.

[Scheider87]

Advanced Programming and Problem Solving with Pascal. Scheider, G. Michael y Bruell, Steven C. John Wiley & Sons, 2nd Edition 1987. Version traducida: Programacion Avanzada y Resolution de Problemas en Pascal. Anaya Multimedia, 1991.

[Scholl91]

Esquemas Algoritmicos Fundamentales: Secuencias e Iteration. Scholl, P. C. y Peyrin, J. P. Editorial Masson, 1991.

[Sethi92]

Lenguajes de Programacion. Conceptos y constructores. Sethi, Ravi. Addison-Wesley Iberoamericana, 1992.

[Slonneger95] Formal Syntax and Semantics of Programming Languages: a laboratory based approach. Slonneger, Kenneth y Kurtz, Barry L. Addison-Wesley Publishing Company, 1995. [TenenbaumSG] Data structures using Pascal. Second edition. Tenenbaum, Aaron M.y Augenstein, Moshe. Prentice-Hall International Editions, 1986. [Terrence96]

Programming Languages. Design and implementation. Third edition. Terrence W. Pratt, Marvin V. Zelkowitz. Prentice-Hall International Editions, 1996.

[TremblaySO]

Structured Pascal. Tremblay, J.P., Bunt, R. y Opseth, L. McGrawHill, 1980. Edicion castellana: McGraw-Hill, 1984.

[Tremblay81]

An Introduction to Computer Science: An Algorithmic Approach. Tremblay, J.P. y Bunt, R. McGraw-Hill, 1981. Edicion castellana: McGraw-Hill, 1982.

[Torres92]

Diseno y Analisis de Algoritmos. Torres, Carmen. Editorial Paraninfo, Madrid, 19992.

[Tucker85]

Programming Languages. Tucker, Allen B. McGraw-Hill 1985.

[Waite85]

Programacion en C. Introduction y conceptos avanzados. Waite, M., Prata, S. y Martin, D. Anaya Multimedia, 1985.

[Watt90]

Programming Languages. Concepts and Paradigms. Watt, David A. Prentice-Hall International, 1990.

[Weiss95]

Estructuras de Datos y Algoritmos. Weiss, Mark Allen. AddisonWesley Iberoamericana, 1995.

[WelshSS]

Pascal. Introduce/on. Welsh, J. y Elder, J. Prentice-Hall 1983.

213

Bibliografía

[WelshQO]

Introducción a Modula 2. Welsh, J. y Eider, J. Prentice-Hall 1990.

[Winston90]

Pascal. Programación estructurada. J. Winston Crawley, William G. McArthur. Prentice Hall Hispanoamericana, S.A., 1990.

[WirthSO]

Algoritmos + Estructuras de Datos = Programas. Wirth, Niklaus. Ediciones del Castillo, Madrid 1980.

[Wirth87]

Algoritmos y Estructuras de Datos. Wirth, Niklaus. Prentice-Hall Hispanoamérica, Méjico 1987.

[WirthSS]

Programming in Modula-2. Wirth, N. Springer Verlag, 1988.

[Yau86]

A Survey of Software Design Techniques. Yau, S. S. y Tsai, J. J. P. IEEE Transactions on Software Engineering, June 1986.