TEMA 3: Datos 3.1 Variables • Una variable es una séxtupla de atributos • Nombre o identificador. • Dirección. • Valor. • Tipo. • Tiempo de vida. • Ômbito. 3.1.1 Nombre o Identificador. • Los identificadores más abundante son los que se refieren a nombres de variables. Pero también hay variables sin nombre. • Reglas que rigen el diseño de los (nombres) indentificadores ( en general). • ¿Cúal es la longitud máxima del identificador ?. • ¿Pueden emplearse caracteres conectores?. • ¿Se distiguen mayúsculas de minúsculas?. • ¿Las palabras especiales son palabras claves o reservadas?. • Id o nombre: cadena de caracteres de longitud variable. Ej: C -> limita la longitud a 31 caracteres. Ada -> no limitan la longitud. C++-> No dice nada en la especificación. • Caracteres conectores: La mayorÃ−a de los lenguajes permiten que se utilice el caracter... • Distinción Mayusculas de Minusculas: C,C++,Modula-2 distinguen el uso de las mayúsculas de las minúsculas ( son case-sensitive). Ej: Suma, suma, SUMA -> 3 identificadores diferentes.Violan el principio de legibilidad. En Modula-2: WriteInt ( nombre de módulo predefinido). Hay que escribirlo asÃ− siempre. • Palabras Clave o reservadas: if, while son palabras especilaes. • Si las palabras son claves; se trata de palabras que sólo son especiales en ciertos contextos. Ej: Fortran : REAL ALTURA ( ALTURA es del tipo REAL) 1 REAL = 8.5 ( aquÃ− REAL es un identificador ) REAL INTEGER INTEGER REAL • Se habla de palabras reservadas cuando son palabras especiales que no pueden emplearse por el usuario como identificadores. En estos lenguajes que usan palabras reservadas hay: • Identificadores predefinidos.Tienen un significado predefinido pero pueden cambiar de usuario. • Ej: llamadas afunciones de librerÃ−a. C-> printf, scanf Pascal-> write, read Ada-> get, put ( aquÃ− en Ada el usuario puede redefinir el sentido de estos identificadores). 3.1.2 Dirección: • Es la dirección de memoria a la que está asociada la variable. Yo puedo tener un identificador “i” en las funciones f1 y f2 y en cada una “i” tiene direcciones de memoria diferentes. • Si f es una función recursiva: En cada activación del procedimiento i tiene una dirección de memoria diferente en la pila A la dirección de memoria también se le conoce como L-valor: v:= E ( para realizar la asignación necesito conocer la dirección de r ). L - valor -> ( el valor de la parte izquierda de una operación de asignación ). • Alias: Tendremos un alias cuando hay más de un nombre de variable para acceder a una misma localización de memoria. • Creación: • Hay lenguajes donde se crean de forma explÃ−cita. Ej: FORTRAN -> EQUIVALENZE COBOL -> REDEFINES • En Pascal y en Ada mediante registros variantes. En C y C++ mediante unions. • Se puede acceder en momentos diferentes a nombres de variables diferentes. Ej: Pascal; Type coordenadas = record case clase:(cartesiana, polar) of cartesiana: (x,y: real); polar:(r,fi: real); 2 end; • Otra forma de crear alias es empleando paso de parámteros por referencia en los subprogramas. ( si pasas un parámetro por referencia ya tienes 2 referencias a esa variable ). • En lenguajes con apuntadores cuando 2 o más apuntadores referencian la misma zona de memoria tambien se están creando alias. NOTA: Para nosotros todos los tipos no estructurados ocupan una celda . 3.1.3 Tipo • El tipo de dato de una variable va a determinar: • El rango de valores. • El conjunto de operaciones definido para ese tipo de dato. Ej: rango de valores: -32768..32767. Integer operaciones : +, -, *, /. 3.1.4 Valor • El valor de una variable es el contenido de la celda de memoria asociada a esa variable. Al contenido de una celda de memoria se le denomina R-valor: v:= i + 1; Para obtener el r-valor de una variable necesito saber primero el L-valor de esa variable. 3.1 Vinculación (Binding) • Cuando se produce una asociación entre una atributo o entre una operación y un sÃ−mbolo. Al momento e n el que se produce esa vionculación es a lo que se denomina tiempo de vinculación. En general vamos a hablar siempre de: • vinculación estática si ésta se produce antes del tiempo de ejecución y permanece inalterable durante toda la ejeccución del programa. • Vinculación dinámica: durante el tiempo de ejecuciòn o cambia en el transcurso del programa. 3.2.1 Vinculación de Tipos. • En cualquier lenguaje de programación antes de la ejecución de una variable hay que saber la vinculación de tipos. • VINCULACIà N ESTÔTICA DE TIPOS. Puede realizarse de manera explÃ−ctita o de una manera implÃ−ctita. • De forma explÃ−cita: El lenguaje tiene una lista de variables a las que se asigna un tipo: int i,j; 3 • De forma implÃ−cita.- No se utilizan estas sentencias, sino que el tipo se asocia a través de determinadas convenciones. La asociación de tipos se produce la 1º vez que aparece la variable. El compilador ya entiende el tipo a través de las convenciones. Ej: BASIC, FORTRAN -> I,...,N I = H * L ( el compilador deduce el tipo de las variables H y L. • Hay otra forma del tipo de declaración implÃ−cita ( ML, HASKELL) donde se emplea el algoritmo de inferencia de tipos. Si no se indica el tipo de las variables, entonces los lenguajes o infieren un tipo, o presentan un mensaje de error . • VINCULACIà N DINÔMICA DE TIPOS • Hay que tener en antes de empezar este punto que las vinculaciones estática e implicita es total • En este caso el tipo no se especifica mediante ningún tipo de declaración, sino cuando se le asigna por primera vez mediante una sentencia de asignación. Ej: APL LISTA <- 8.1, 3.0, 0.5, 6.25. ( lista de cuatro elementos de números reales ). LISTA <- 3. ( un número entero ). • Ventaja: Este tipo de programación da gran flexibilidad al programador. • Desventajas: • No se detectan muchos errores de tipos. Ej: i, x -> enteros. y -> real. I:= y / i:= x No da error en vinculación dinámica de tipos , convierte a i en real. Existen también coacciones de tipo: i:= y. • El coste de implementación en tiempo de ejecución (elevado coste de asignación y desasignación de espacio). 3.2.2 Vinculación de espacio y tiempo de vida. 4 • La vinculación de espacio significa que desde algunas zonas de memoria disponibles se vinculan celdas de memoria a un a variable: asignación. El proceso de desasginación: se devuelve a una zona de memoria libre el espacio que hasta entonces estaba asignado a una variable. • Tiempo de vida de una variable: Es el tiempo durente el cual la variable está vinculada a un lugar especÃ−fico de la memoria ( tiempo que transcurre entre la asignación y la desasignación). • Vamos a clasificar la vinculación de espacio atendiendo al tiempo de vida de la variable. • Variables estáticas. La vinculación de espacio se produce antes de que se comience a ejecutar el programa. Permanecen vinculadas a las mismas celdas hasta que acaba la ejecución del programa. Las variables globales son variables estáticas. Muchas veces en los subprogramas nos interesa que la variable recuerde el valor entre llamadas diferentes: "sensibles a la historia". En C las variables locales se puenden transformar en esáticas a través de static. Sus ventajas son: • La eficiencia • Todas estas variables pueden tener un direcciónamiento directo. • No hay sobrecarga de asignación o desasignación en tiempo de ejecución. Su desventaja: • Falta de flexibilidad, por ejemplo no se permite la recursividad. • Variables dinámicas en pila. En este tipo de veriables la vinculación se produce en el momento de la elaboración de la sentencia de declaración. Entendemos elaboración de la sentencia de declaración cuando en el momento de la ejecucuión de sentencia se vincula espacio a las variables que se indican. Esta elaboración está sucediendo en tiempo de ejecuacuón. Ej: Pascal VAR: sección de declaración -> se elabora antes de la ejecución del código. BEGIN sección del código -> variables locales. END Por tanto la asignación de espacio ocurre en tienpo de elaboración y la desasignación ocurre al finalizar el subprograma (procedimiento o función). • Se denominan de pila porque el espacio se asigna en la pila de ejecución. Cada vez que se ejecuta un procedimiento aparece en la pila un bloque de asignación. Variables locales- > Se declaran en los subprogramas. Ventajas: • Si todas la s variables son estáticas, debemos guartadar en la zona de memoria espacio para todas ellas. • Si el lenguaje proporciona variables dinámicas de pila, estoy utilizando el mÃ−nimo espacio para todas las variables, a medida que voy utilizando procediminentos voy haciendo reserva de pila ( zona 5 común para todas las variables de pila). • Permiten recursión. Desventajas: • Sobrecarga relativa en tinepo de ejecucuón. • No son sensibles a la historia, no conocemos el valor que tenÃ−an al hacer una siguiente llamada. • Variables dinámicas de Heap. • Heap: colección de celdas de memoria que se caracteria por tener una estrucutura muy desorganizada debido a que tiene un uso de sus celdas de manera impredecible. • Variables dinámicas del Heap: • ExplÃ−citas: Son variable sin nombre. Su asignación o desasiganción se realiza en tiempo de ejecución, decidiendolo el programador ( coloca una instrucción allÃ−; al efecto ).Se referencian mediante apuntadores Ej: Creacción / destrucción mediante operador: C ++, new / delete. Creacción / destrucción mediante función de librerÃ−a: C; Pascal: malloc() / free(); new() / dispose () Ej: C++ cnt *apentero; ......................... apentero = new int; // Reservamos una celda del Heap .......................... delete apentero; // Se desvincula ( la celda pasa a estar libre ). • Se usan para crear estructuras del tipo lista, árboles... Desvetajas: Sobrecarga en tienpo de ejcución (El coste en tiempo de ejecución son la asigneción + la desasigneción + la referencia) y la dificultad de uso. • Implicitas: • La vinculación del espacio se produce casa vez que se le asignan valores a las variables ( no se produce por la instrucción que coloca el programador ). 6 Ej: APL Lista <- 4.0, 3.5, 7.12 Lista <- 8. Ventaja: Flexibilidad en la programación. Desventajas: - Sobrecarga que se produce en tiempo de ejecución. - Se dejan de detectar algunos errores de tipo. 3.3 Tipos 3.3.1 Comprobación de Tipos. • Los subprogramas son operadores; por tanto los parámetros de los subprogramas son los operandos • Asignación -> operador binario. Bajo estas suposiciones definimos: • Comprobación de tipos: es la actividad que nos asegura que los operandos de un operador son de tipos compatibles. • Tipos compatibles: si se trata de un tipo legal paraa ese operador o bien atendiendo a algunas reglas del lenguaje puede ser convertido (de manera Ã−mplicita atendiendo siempre a reglas del lenguaje y empleando un código que genera el compilador a tal efecto ), en un tipo legal. Esta conversión se llama coacción. ( coerción). • ¿ Cuándo se produce un error de tipos ?. Cuando se emplea un tipo inadecuado del operador que se está utilizando. • Vinculación estática de tipos a variables .- comprobación de tipos puede ser estática. ( podemos comprobarlo también en tiempo de compilacción ). • Vinculación dinámica .- La comprobación debe ser dinámica. • Pascal , Ada, C permiten estructuras de tipo registros variantes o uniones-> la comprobación dinámica de tipos. Pascal y C no realizan una comprobación de tipos para sus registros variantes con lo cual nos puede dar errores de ejecución o podemos recuperar algo que no es lo que querÃ−amos recuperar ( Ada sÃ− ). 3.3.2 Asignación fuerte de tipos ( Strongly typed ) • Un lenguaje tiene asignación fuerte de tipos si cada identificador del lenguaje tiene asociado un sólo tipo, y este tipo se conoce en tiempo de compilación. ( aquellos lenguajes que realizan una vinculación estática de tipos ). Esta definición no engloba a registros variantes o uniones. • Un lenguaje tiene asignación fuerte de tipos si los errores de tipos se detectan siempre. Esta definición no dice si la vinculación va en tiempo de compilación ó ejecución, es una definición más amplia. 7 • Pascal no es un lenuaje de asignación fuerte de tipos porque posee registros variantes y no realiza una comprobación dinámica de tipos para estos registros variantes y por tanto no detecta errores para esta estructura en cuanto a tipos. • Ada. Resuelve el problema de los registros variantes realizando una comprobación dinámica de tipos, pero, posee una función de librerÃ−a que nos permite extraer el contenido de cualquier zona de memoria como un string de bits que puedo asignar a cualquier variable- > estamos escapando de la asignación fuerte de tipos. Ada no tiene asignación fuerte de tipos. • C. No tiene asignación fuerte de tipos ya que tiene unions. C permite funciones con parámetros sobre los que no se realiza comprobación de tipos. Ej: printf("%s", 25); // No da error en tiempo de compilación pero sÃ− en tienpo de ejecución. ( No se detectan todos los errores de tipo ). • Lenguajes con asignación fuerte de tipos: ML, Haskell realizan un comprobación fuerte de tipos. 3.3.3 Compatibilidad de Tipos (Equivalencia de tipos ). • Si dos variables son compatibles sus valores se pueden asignar ( el valor de una se puede asignar a la otra y viceversa ). • Para saber si dos tipos son compatibles existen 2 métodos: • Compatibilidad de tipos normal: Dos variables tiene una compatibilidad de tipos normal si se declaran en el mismo lugar o en declaraciones que usan el mismo nombre de tipo. • Compatibiliodad de Tipos Estructural: Si poseen la misma estructura. • C.T. NOMINAL. • Fácil de implementar ( lo único que hay que hacer es comparar el nombre de tipo de ambas variables). • Puede llevar una interpretación estricta: Ej: Pascal Type tipo_indice = 1..100; Var contador: integer; indice: tipo_indice; Son incompatibles l ... • Otro problema es el paso de estructuras como parámetros. Ej: Pascal (pasar un array). 8 Procedure x (Var a:array[1..10] of integer). { Esto da un error de tipos} • COMPATIBILIDAD DE TIPOS ESTRUCTURAL • Se comprueba la estructura • ¿Dos registros con igual estructura pero con nombres de campos distintos son incompatibles ?. • ¿Dos arrays son cammpos distintos pero del mismo tipo son incompatibles ?. [1..10] of integer [0..9] of integer • ¿Enumerados con igual número de elementos pero distintos nombres? • Estos tres últimos puntos los tiene que decidir el diseñador del lenguaje, mientras que el lenguaje tendrá que dar la respuesta. Ej: Type Celsius = real; Farenheit = real; Var C: Celsius; F: Farenheit; c:= t; f:= c; La compatibilidad nominal no permitirÃ−a esto. Vamos a ver que ocure en Pascal, Ada y C: • Pascal. Mezcla de la C.T .Nominal y esstructural: Type tipo1 = array[ 1..10] of integer; tipo2 = array[ 1..10] of integer; tipo3 = tipo2; 9 i de tipo 1 incompatibles j de tipo2 j de tipo2 compatibles. Aplica la equivalencia de declaración. k de tipo3 • Ada. Emplea C.T. Nominal en general. Ofrece 2 constructores de tipos: - Subtipo ( C.T.Estructurado). - Tipo derivado. • Tipo derivado.- Se define a partir de otro tipo. El que se está definiendo resuelta incompatibles con el predecesor ( pero hereda todas la propiedades de éste ). Por ejemplo: Type celsius is new float; Type farenheit is new float; Celsius es incompatinble con float. Farenheit es incompatible con float. Farenheit es incompatible con celsius. ( Las constantes se tratan aparte ). Constantes 6.0, 8.7 -> se entienden que son de un ... • Subtipos Subtipo natural is integer range 0.. integer'last. Subtipo calificación is integer range -3...+3. Natural no es incompatiblecon integer. Calificación no es incompatible con integer. Calificación no es incompatible con natural. Todos los tipos tiene compatibilidad estructural: c:= n AquÃ− hay una C.T.Estructural. En compilación lo pasa, luego en tiempo de ejecución deberá comprobar los rangos. Ej: 10 function "**" ( i: integer; n: natural) return integer; j**k ( k puede ser: natural, calificacción o entero. • C: Se emplea C.T.Estructural excepto para registros y uniones ( que se emplea equivalencia de declaración ): Typedef es un nuevo nombre paraun tipo ya existente. Hay lenguajes que permiten definir variables de tipos anónimos. Var a: array[1 ..10] of integer; b: array[1..10] of integer; array es un tipo anónimo, a y b son tipos anonimos incompatibles Hay lenguajes, a,b: array[1..10] of integer; Incompatible en Ada. Para que sean compatibles habrÃ−a que definir un tipo de manera expÃ−cita, pero es compatible con Pascal. 3.4 Ômbito • El ámbito de una variable es el rango de sentencias en que dicha variable es visible. Una variable es visible en una sentencia si puede regerenciarse en la misma • La reglas de ámbito en un lenguaje son muy importanrtes. La forma de esasa reglas va a determinar que un identificador en un programa se asocie a una variable o se asocie a otra. - Variables locales. - Variables no locales. La variable no se declara en ese subprograma. 3.4.1 Ômbito Estático • Método de vinculación de nombre de identificadores a variables no locales que ocurre en tiempo de compilación. • El ámbito son las variables declaradas como locales al procedimiento más la colección de todas las variables del ámbito de subprograma antecesor que son visibles para éste. El ámbito, en este caso, es necesitado durante la compilación. • En Pascal, el ámbito es creado sólo por las declaraciones en los procedimientos. El ámbito incluye las variables locales más todas las variables declaradas en los procedimientos en los cuales el conjunto de sentencia es necesitado y más las variables declaradas en el programa principal. • ¿Cómo se determinan los atributos de una variable en un lenguaje con ámbito estática?(Transparencia) • Buscar la declaración local en el subprograma 1 y si no se encuentra. 11 • Se continúa la búsqueda en el subprograma que contiene o declara al subprograma, este programa es el padre estático de subprograma1 sino continuarÃ−a la búsqueda en todos los antepasados estáticos de sub1 • Termina la búsqueda cuando se encuentra la declaración de x o se ha llegado a inspeccionar la unidad mayor. • Los estáticos pueden ocultar variables antepasadas. • Ada - Aunque una variable esté oculta permite hacer referencia a esta variable: Nombre_unidad_programa.nombre_variable • Algunos lenguajes herederos permiten definir nuevos ámbitos estáticos en mitad del código ejecutable (En éstos ámbitos puede haber nuevas variables locales). • Bloque: Fracción de código en la que puede definirse variables locales. Da orÃ−gen a los lenguajes estructurados en bloques. • Pascal -> Lenguaje estructurado en bloques, todos los bloques son procedurales (procedimientos o funciones ). • C -> Cualquier grupo de sentencias entre llaves, las primeras sentencias de ese bloque pueden ser declaraciones (estas son variables dinámicas de pila -> Se asigna espacio a las variables al entrar en cada bloque • Ada -> En cualquier punto del código puedo escribir Declare temp: integer ; <- Temp es vble. Din. Pila Begin temp:=x x:=y y:=temp end • El ámbito estático proporciona un método de acceso a variables no locales que es el apropiado en la mayorÃ−a de las ocasiones. • Desventajas : • Acceso a demasiados datos (todas las vbles. Declaradas en el principal son visibles en todos los procedimientos hijos sintácticos de él ) • Los cambios pueden ser complicados Principal principal A CAB 12 DCDE B E Ejemplos de soluciones para cambios: • Mover E dentro de D -> deja de tener acceso al ámbito de B con lo que podrÃ−a no funcionar. • Mover las variables de D que necesite E al principal -> estas variables también son accesibles a A y a B (demasiados datos ) y puede que las vbles que yo necesite es x declarado en D y la muevo al principal y existe otra x en A habrÃ−a que renombrar las variables ( si llamamos desde D llamo a la de A y no al principal) Hacer cambios para que un procedimiento pueda llamar a otro enun lenguaje de ámbito estático puede se complicado. La tendencia de los programadores es usar más variables globales de las que necesita. 3.4.2 Ômbito Dinámico • Son las variables declaradas localmente más las variables de todos los subprogramas que están activos de forma concurrente. • Lenguajes como APL. • Se basa en la secuencia de llamadas a subprogramas • Solamente puede determinarse el ámbito dinámico en tiempo de ejecución. Significado de x: • Comienza la búsqueda en las declaraciones locales. • Se busca en las declaraciones de su padre dinámico (procedimiento que le llamó), • sino la búsqueda prosigue con los antepasados dinámicos. Ej. (transparencia) . mayor->sub2->sub1 x es real, es la vble. Declarada en sub2 (tenemos que saber cual es el padre dinámico y se conoce en tiempo de ejecución) . mayor->sub1 x es integer, es la variable declarada en mayor • Desventajas : • Las mismas referencias pueden tener significados diferentes en ejecuciones diferentes. (Todas las v. Locales de todos los procedimientos son visibles en los otros procedimientos a los que llama => acceso a demasiados datos) 13 • Accesibilidad desde todos los subprogramas a los que se llama => menor fiabilidad • Imposibilidad de hacer una composición de tipos estática para las v. No locales. • Los programas son más difÃ−ciles de leer y seguir • Ventajas : • El subprograma llamado hereda el contexto de sus llamadores. (Def. Ambito dinámico: Variables declaradas localmente más las variables de todos los subprogramas que están activos de forma concurrente.) 3.4.3 Entorno de Referencia • Variables que se pueden referenciar desde un lugar determinado. • Conjunto de todos los identificadores visibles en dicha sentencia. • Lenguaje de ámbito estático: Vbles. Locales + vbles. De los antepasados estáticos exceptuando las vbles. Que se ocultan • Lenguaje de ámbito dinámico: vbles. Locales + V. Declaradas en los antepasados dinámicos. Variables que se pueden referenciar desde un lugar determinado. 3.5 Constantes (Constantes con nombre) Es un objeto al que se vincula a un valor sólo en el momento en el que se vincula el espacio. (El valor de una constante no se puede modificar por asignación o sentencia de entrada) • Una constante es una variable que está vinculada únicamente a un valor. • Su valor no puede ser cambiado mediante una sentencia de asignación o cualquier otre sentenmcia. • Las constantes son útiles para permitir una mejor legibilidad y fiabilidad. • Otras ventajas de usar constantes es en programas que procesan un número fijado de valores de dato. Inicialización de Variables 3.6 • La discusión de vincular valores a constantes conduce naturalmente al tópico de inicialización de variables, porque la vinculación de un valor a una constante es el mismo proceso, excepto porque éste es permanente. • Es conveniente que las variables tengan un valor antes de utilizarse. • Inicialización : Vinculación de un valor a una variable en el momento en el que se vincula el espacio. • C-> Puede especificarse la inicialización en el momento de la declaración Int i=10 • Las variables estáticas se inicializan una sola vez (cuando se asigna el espacio) las variables dinámicas de pila se inicializan también cuando se ejecuta el bloque que contiene esa declaración. 14 Variables no Inicializadas: C -> int i; • Aproximaciones a las soluciones: • Se ignora el problema (bajo responsabilidad del programador) • No permite el uso de valores no incializados (averiguándolo en tiempo de compilación analizando la parte derecha de todas las variables y comprobar que tienen valor)=> mayor costo • Todas las declaraciones inicializan el espacio asignado a un valor apropiado por defecto para cada tipo básico. • Utilizan un valor especial para cada tipo: omega (Cuando una variables tiene el valor omega es que no está inicializada) :I+3 > omega (i omega) • Modula2 y PASCAL -> Ignoran el roblema • Ada -> Los apuntadores se inicializan a NULL y se ignora el problema para el resto de tipos • C-> Las vbles. Estáticas se inicializan a una forma apropiada de cero y se ignora el problema para el resto de variables. Tema 3: Datos LPR 1 13 • 15