Post on 04-Oct-2020
1
Capítulo II
Conceptos Fundamentales de Lenguajes de Programación
2.1 Sintaxis y Semántica
Descripción de sintaxis y semántica, análisis sintáctico y semántico
2
Definición: Sintaxis y Semántica
• SINTAXIS: forma de expresiones, sentencias y unidades de programa
• SEMÁNTICA: significado de estas expresiones, sentencias y unidades de programa
• Ejemplo:while (<expr>) do <sentencia>
Elementos Sintácticos
• Conjunto de caracteres• Identificadores• Símbolos de operadores• Palabras claves y reservadas• Comentarios• Blancos, delimitadores y paréntesis• Expresiones• Sentencias
3
Descripción de Sintaxis
• Definición de Lenguajes– Reconocimiento (reconoce si string de entrada
pertenece al lenguaje)– Generación (genera strings que pertenecen al
lenguaje)• Métodos formales de descripción
– Backus Nauer Form (BNF)– Extended BNF (EBNF)
Elementos de BNF
• Símbolos no terminales– Abstracción que representa una regla
• Símbolos terminales– Tokens de las reglas
• Símbolo de partida– Símbolo no terminal que permite generar un
programa
4
Ejemplo de Gramática
<program> ::= begin <stmt_list> end
<stmt_list> ::= begin <stmt> ; <stmt_list> end
<stmt> ::= <id> := <exp>
<id> ::= A | B | C
<exp> ::= <id> | <exp> + <exp> | <id> * <exp> | (<exp>)
Parse Tree
• Expresión A := A*B + C
A
id
A B
C
*
id id
exp +
id
exp:=
stmt
exp
exp exp
5
Ambigüedad
• Para la expresión A := A*B + C existen dos parse tree:
A
id
A B
C
*
id id
exp +
id
exp:=
stmt
exp
exp exp
A
id
A
*
id
exp
B C
+
id id
exp
:=
stmt
exp
exp
exp
Otro ejemplo de ambigüedad:
if condition
if_stmt
stmtthen
if_stmt
if condition stmtthen else stmt
if_stmt
if condition stmtthen else stmt
if condition
if_stmt
stmtthen
Dada la siguiente gramática:
<if_stmt> ::= if <condition> then <stmt> | if <condition> then <stmt> else <stmt>
<stmt> ::= <if_stmt> | … Reconocer: if (C1) then if (C2) then S1 else S2
6
Resolución de Ambigüedad
• Caso de operadores:– Definir precedencia– A igual precedencia, definir asociatividad por
izquierda o derecha• Caso del if:
– Asociar el else con el último if
Extended BNF (EBNF)
• Elemento opcional se indica con [ …]• Alternativa puede usar | en una regla• Repetición de elementos se indican con
{ … }
7
Ejemplos de EBNF
<if_stmt> ::= if <condition> then <stmts> [ else <stmts>]
<identifier> ::= <letter> {<letter> | <digit> }
Grafos Sintácticos<if_stmt> ::= if <condition> then <stmts> {<else_if> }
[ else <stmts> ] end if
<else_if> ::= elseif <condition> then <stmts>
conditionelseif then stmtselse_if
conditionif then stmtselse_stmt
else
end if
stmtselse_if
8
Proceso de Compilación
Análisis LéxicoAnálisis Léxico
Análisis SintácticoAnálisis Sintáctico
Análisis SemánticoAnálisis Semántico
Generación de CódigoGeneración de Código
Programa Fuente
Programa Objeto
Tokens léxicos
Parse tree
Código intermedio
Reconocimientodel programafuente
Tabla de símbolos
2.2 Nombres, Ligado y Ámbito
Ligado estático y dinámico, reglas de ámbito y prueba de tipos.
9
Conceptos
• Nombres e Identificadores• Variables• Tipos• Ámbito• Constantes
Nombres
• Identificador que designa en el lenguaje un elemento u objeto de programa– (e.g. variable, tipo, constante, función, etc.)
• Aspectos de diseño:– Largo (con significativo) del nombre– Tipos de caracteres aceptados (e.g. conector _)– Sensibilidad a mayúsculas y minúsculas– Palabras reservadas
10
Variables
• Abstracción de un objeto de memoria, que tiene los siguientes atributos:– Nombre (identificador)– Dirección (l-value: ¿Dónde está localizada?)– Valor (r-value: contenido)– Tipo (tipo de dato que almacena y operaciones válidas)– Tiempo de vida (¿Cuándo se crea y se destruye?)– Ámbito (¿Dónde se puede referenciar?)
Dirección de una Variable
• Un nombre puede ser asociado con diferentes direcciones en diferentes partes y tiempo de ejecución de un programa– El mismo nombre puede ser usado para referirse a
diferentes objetos (direcciones), según el ámbito.– Diferentes nombres pueden referirse a un mismo objeto
(alias), lo que puede provocar efectos laterales.• Ejemplo:
– Union y punteros en C y C++– Variables dinámicas en PERL
11
Ligado (Binding)
• Definición: Proceso de asociación de un atributo auna entidad del lenguaje.– Variable: Dirección, tipo → Nombre– Operador: Código → Símbolo
• El tiempo de ligado se refiere al instante en que sucede la asociación:– Estática: diseño o implementación del lenguaje;
compilación, enlace o carga del programa– Dinámica: ejecución del programa
a) Ligado de Tipos a Variables
• Variables deben ser ligadas a un tipo antes de usarlas.
• Ligado Estático: con declaración explícita o implícita– Lenguajes modernos usan sólo declaración explícita– C y C++ hacen la diferencia entre declaración y definición
• Ligado Dinámico: en el momento de la asignación– Ventaja: Permite programar en forma genérica– Desventaja: disminuye capacidad de detección de errores y
aumento del costo (ejecución)
12
b) Ligado a Memoria y Tiempo de Vida de Variables
• El carácter de un lenguaje está en gran medida determinado por cómo se administra la memoria.
• A la variables se les puede asignar y liberar memoria de un espacio común (liberación implícita requiere de un recolector de basura).
• Las variables escalares, según tipo de ligado, se clasifican en:– Estáticas– Dinámica de stack– Dinámica de heap (explícita o implícita)
b.1) Variables Estáticas• Ligadas antes de la ejecución• Ventajas
– Útil para variables globales– Para variables sensibles a la historia de un subprograma
(e.g. uso de static en variables de funciones C y C++)– Acceso directo permite mayor eficiencia
• Desventajas– Falta de flexibilidad– No soportan recursión– Impide compartir memoria entre diferentes variables
13
b.2) Variables Dinámicas de Stack• Ligadas (a la memoria) en el momento en que la
ejecución alcanza el código asociado a la declaración.
• Típicamente el tipo se liga estáticamente.• En Pascal se asigna memoria en el momento de
ingresar al bloque que define el ámbito de la variable.
• En C y C++ las variables son por defecto de este tipo (denominadas variables automáticas).
b.3) Variables Dinámicas de Heap(explícita)
• La memoria se asigna y libera en forma explícita por el programador, usando un operador del lenguaje o una llamada al sistema– C++ dispone del operador new y delete– C usa llamada al sistema malloc() y free()– Java no permite la liberación explícita
• Ventaja: útil para estructuras dinámicas usando punteros.
• Desventaja: Dificultad en su uso correcto
14
b.4) Variables Dinámicas de Heap(implícita)
• Se ligan al heap sólo cada vez que ocurre una asignación (usadas en ALGOL 68, APL y PERL)
• Ventajas:– Alto grado de flexibilidad
(un nombre sirve para cualquier cosa)– Permite escribir código genérico
• Desventaja:– Alto costo de ejecución– Pérdida en la capacidad de detección de errores
Tipo de Datos
• Para generalizar, supongamos:– La asignación es un operador binario con una variable y una
expresión como operandos– Un subprograma es un operador cuyos parámetros son sus
operandos
• Verificación de tipo es asegurar que los operandos de unoperador son de tipo compatible.
• Un tipo compatible es uno legal o que mediante reglas el compilador lo puede convertir a legal.
• La conversión automática de tipo se denomina coerción
15
Verificación de Tipo• Si todos los tipos se ligan estáticamente⇒ verificación de tipos se puede hacer estáticamente.
• Ligado dinámico requiere realizar una verificación de tipo en tiempo de ejecución.
• Lenguajes modernos prefieren una verificación de tipo estática
• Verificación de tipo se complica cuando se permite almacenar valores de diferente tipo en una variable=> (e.g. Registro con variante en Pascal y Union en C).
Tipificado Fuerte(strong typing)
• Un lenguaje es de tipificado fuerte si permite detectar siempre los errores de tipo:– Cada nombre debe ser ligado estáticamente a un tipo– Ligado dinámico a memoria requiere verificación en
tiempo de ejecución
• Ejemplos:– ¡Pascal casi lo es! (registro con variante es excepción)– C y C++ no lo son (parámetros en funciones y union)– Java si lo es (permite forzar conversión de tipo)
16
Compatibilidad de Tipos
• Compatibilidad de nombre– Las variables están en la misma declaración o usan el
mismo nombre de tipo– Fácil implementación, pero muy restrictivo
• Compatibilidad de estructura– Si los tipos tienen la misma estructura– Más flexible, pero de difícil implementación
• Algunos lenguajes permiten forma intermedia!(e.g. Equivalencia declarativa en Pascal)
Ejemplo: PascalTYPE
mes_t = 1..12;VAR
indice : mes_t;contador: integer;
{otro ejemplo}
TYPEcelsius = real;fahrenheit = real;
{otro ejemplo}
TYPEtipo1 = ARRAY [1..10] OF integer;tipo2 = ARRAY [1..10] OF integer; {tipo1 <> tipo2}tipo3 = tipo2; {equivalencia declarativa}
17
Ejemplo: C y C++
typedef struct {int c1; real c2 } s1; typedef struct {int c1; real c2 } s2; s1 x; s2 y = x; /* error de
compatibilidad de tipos*/ typedef char* pchar; pchar p1, p2; char* p3 = p1;
Ámbito (Scope)
• Rango de sentencias en el cual un nombrees visible.
• Nombres pueden ser sólo referenciadasdentro del ámbito.
• Nombres no locales son los que son visibles dentro de un bloque, pero han sido declarado fuera de él.
18
a) Ámbito Estático
• Ámbito estático: puede ser determinado antes de la ejecución (El primero: ALGOL)
• Usado en la mayoría de los lenguajes imperativos.
• Es normal permitir el anidamiento en una jerarquía de ámbitos (excepto: C, C++, Java y Fortran), que define una ascendencia estática
Ejemplo: Pascalprogram main;var x: integer;
procedure p1;begin { p1}… x …end; {p1}
procedure p2;var x: integer;begin { p2}… x …end; {p2}
begin {main}…end. {main}
p1
p2
main
19
Ejemplo: C
extern int i;int j = 100;static int k;
void f1 (…){ int i,m; … }
void f2 (…){ int static k=0;
if (!k) { printf(“primera vez\n”); k=1}; for (int j=0; j++; j<99) {¨…};}
b) Ámbito Dinámico
• Ámbito de nombres no locales está definido por secuencia de llamadas a subprogramas.
• Anidamiento de llamadas define ascendencia dinámica
• Problemas:– No permite proteger variables locales– No se pueden verificar estáticamente los tipos– Programas son difíciles de leer y más lentos
20
Ejemplo: Ambito Dinámicopr og ra m m ai n;va r x: in te ger ;
p roc ed ur e p 1;b egi n { p1}… x …e nd; { p1 }
p roc ed ur e p 2;v ar x: i nte ge r;b egi n { p2}… x …e nd; { p2 }
be gi n {ma in }…en d . { mai n}
main → p2 → p1
main::x p2::x
Constantes con Nombre
• Son variables que se ligan a un valor sólo en el momento de ligar la memoria; – luego su valor permanece constante
• Sirven para mejorar la lectura• Inicialización puede permitir expresiones
21
Ejemplo: C y C++
#define MAX 100 const int max = 100+MAX; for (int i=0;i++; i<MAX)
{ … };
for (int j=0;i++; i<max){ … };
2.3 Tipos de Datos
Tipos de datos simples, estructurados y punteros
22
2.3.1 Tipos de Datos Simples
Tipos primitivos, representación, tipos definidos por el usuario
Introducción
• ¿Cómo calzan los tipos de datos con problemas del mundo real?
• Evolución de los tipos:• Números enteros y reales• Arreglos y Registros• Cadenas de Caracteres• Definidos por el usuario• Tipo de dato abstracto
23
Tipos Ordinales
• Un tipo ordinal es aquel que puede ser asociado a un número natural (ordenados)
• Tipos ordinales primitivos:– entero, caracter y booleano
• Tipos ordinales definidos por el usuario:– 1) Enumerados– 2) Subrangos
Representación de Números
• Características de Representación– Conjunto Finito– Rango de representación y precisión depende
del largo del registro• Tipos de Representación
– Números enteros– Números de punto fijo– Números de punto flotante
24
Tipo Enumerado
• Se enumeran todos los posibles valores a través de constantes literales.
• Relación de orden permite definir operadores relacionales y predecesor y sucesor.
• Mejoran facilidad de lectura y fiabilidad• Normalmente no se usan en E/S• Algunos lenguajes como ADA permiten
sobrecarga de constantes literales
Ejemplo : C y C++
<enum-type> ::= enum [<identifier>] { <enum-list> }<enum-list> ::= <enumerador> | <enum-list> , <enumerador><enumerador> ::= <identificador> | <identificador> = <constant-exp>
enum color {rojo, amarillo, verde=20, azul};color col = rojo;color* cp = &col;
if (*cp == azul) // ...
25
Tipo Subrango
• Subsecuencia contigua de un tipo ordinal• Introducido por Pascal y usadas en Modula-
2 y ADA• Mejora lectura y fiabilidad• Ejemplo: Pascal
typemayuscula = ´A´..´Z´;
indice = LUNES .. VIERNES;
Tipos de Datos Primitivos
• Numérico– Entero (e.g. C permite diferentes tipos de
enteros: signed, unsigned, short, long) – Punto flotante (e.g C permite float y double)– Decimal (típicamente 4 bits por dígito decimal)
• Booleano (típicamente ocupa un byte)• Caracter (típicamente un byte y código ASCII;
Java usa Unicode con 2 bytes)
26
2.3.2 Tipos de Datos Estructurados
Arreglo, Arreglos dinámicos y Registros
Tipo Arreglo
• Es un tipo estructurado consistente en un conjunto ordenado de elementos que se identifican por su posición relativa mediante un índice.
• Existe un tipo asociado a los elementos y al índice.• Índice se escribe entre:
– paréntesis redondo (Fortran y ADA) o– cuadrado (Pascal, C, C++, Modula-2 y Java).
• Verificación de rango del índice mejora fiabilidad (Pascal, Ada y Java lo hacen).
27
Arreglos: Categorías de Ligado
• 1) Arreglo Estático: rango de índice y memoria ligado antes de la ejecución (ejecución eficiente)– e.g. Único tipo en Fortran77.
• 2) Arreglo Dinámico Fijo de Stack : rango de índice ligado estáticamente, pero memoria se asigna dinámicamente (uso más eficiente de la memoria)– e.g. Arreglos definidos dentro de un procedimiento o
función en Pascal y C (sin static).
... Categorías de Ligado
• 3) Arreglo Dinámico de Stack: Rango de índice y memoria asignada dinámicamente; permanece fijo durante tiempo de vida de la variable (más flexible).– e.g. ADA y algunas implementaciones de C y C++
• 4) Arreglo Dinámico de Heap: Tamaño puede variar durante tiempo de vida (la mayor flexibilidad)– e.g. C y C++ con malloc (con restricciones) y PERL
28
Ejemplo:Arreglos Dinámicos de Stack
void foo(int n){
int a[n];
for (int i=0; i<n; i++) { ... }...
}
Ejemplo: Arreglos Dinámicos de Heap
• C y C++
• Perl
char *str, *s, *t;
…str = malloc(strlen(s) + strlen(t)+1);strcat(strcpy(str, s), t);
@a = split (“ “, $text);
29
Arreglos Multidimensionales
• A partir de Fortran IV se permite declarar hasta 7 dimensiones
• Pascal permite sólo dos dimensiones
• C y C++
TYPEmatriz = ARRAY [subindice, subindice] OF real;
real matriz [DIM1][DIM2];
Inicialización de Arreglos
• Fortran
• ANSI C y C++
• Pascal y Modula-2 no lo permiten
INTEGER MES(12);DATA MES /31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31/
char *mensaje = “Hola mundo\n”;char *dias[] = {“lu”, ”ma”, “mi”, “ju”, “vi”, “sa”, “do”};
30
Operadores con Arreglo
• APL y Fortran 90 proveen varios operadores
• ADA permite la asignación• Pascal y C y no tienen soporte especial
(sólo selector con subíndice [])• C++ permite definir una clase arreglo por el
usuario y operadores tales como subíndice, asignación, inicialización, etc.
Implementación de Arreglos
• La memoria es un arreglo unidimensional de celdas:– Un arreglo es una abstracción del lenguaje– Un arreglo debe ser mapeado a la memoria
• Ejemplo: dirección de lista[k]
• Arreglos bidimensionales se almacenan como fila de columnas, o viceversa
dir(lista[0]) + (k)*tamañodir(lista[bajo]) + (k-bajo)*tamaño
31
Arreglos Asociativos
• Arreglo no ordenado de elementos que son accedidos por una clave
• Cada elementos corresponde a un par ordenado (clave, dato)
• PERL provee esta estructura con el nombre de hash: %dias = (
1 => “Enero”,…12 => “Diciembre”
);print $dias{7};
Tipo Registro
• Permite composición heterogénea de elementos de datosCada elemento se identifica por un nombre (campo o miembro)– Introducido por COBOL (data division)– En C equivale a struct– Concepto de clase en O-O soporta registros
(e.g. C++ y Java)
32
Ejemplo: Registros en C y C++ (struct)
struct empleado_t {struct {
char primer[10];char paterno[10];char materno[10];
} nombre;int sueldo;
}
empleado_t pelao, guaton;
guaton.sueldo = 550000;strcpy( pelao.nombre.primer, “Juan”);
Ejemplo: Registros en PascalTYPE empleado_t = RECORD
nombre :RECORD primer: PACKED ARRAY [1.10] OF char;
paterno: PACKED ARRAY [1.10] OF char; materno: PACKED ARRAY [1.10] OF char;
END {nombre}; sueldo : integer
END;VAR
pelao, guaton : empleado_t;
BEGIN…
pelao.sueldo := 550000;
33
Referencias Elípticas• Algunos lenguajes sólo permiten referencias de
calificación completa (C y C++)• Otros permiten referencias elípticas, una forma más
conveniente de programar (Pascal, Cobol y PL/I)• Ejemplo: Pascal
pelao.nombre.primer := ´Juan ´; pelao.nombre.paterno := ´Perez ´; pelao.nombre.materno := ´Machuca ´;
WITH pelao.nombre DO BEGIN
primer := ´Juan ´; paterno := ´Perez ´;
materno := ´Machuca ´; END
2.3.3 Otros Tipos de Datos Estructurados
Strings, Unión y Conjunto
34
Cadena de Caracteres (String)
• Principalmente para la comunicación máquina-usuario y para manipulación de textos– Mejora la facilidad de escritura
• ¿Es una cadena un tipo primitivo? – Algunos lenguajes lo proveen como tipo (ADA, Java y Perl)– Otros sólo como arreglo de caracteres (C, C++ y Pascal)
• ¿Puede el largo variar dinámicamente?
Strings: Operaciones Básicas
• Asignación• Comparación• Concatenación• Largo• Transformación (e.g. de string a entero)
35
Ejemplo de String en C
char str[20];
…if (strcmp(str,”Hola”){
…else {
…}
Strings: Calce de Patrones (Pattern matching)
• Lenguajes que incorporan facilidades: Snobol4 y Perl.
• Perl hace uso de expresiones regulares– Ejemplo:– /[A-Za-z][A-Za-z\d]+/ permite calzar un
identificador– /^(\s+)\s+(s+)\s+(\s+)$/ permite calzar tres
palabras
36
Diseño de String
• Diseño de string considera:– Largo estático (Fortran77, Pascal y ADA)– Largo dinámico limitado (e.g. C y C++)– Largo dinámico (e.g. Snobol y Perl)
• Último es el más flexible, pero es más costoso de implementar y ejecutar
Tipo Union
• Permite almacenar diferentes tipos de datos en diferentes tiempos en una misma variable.
• Alternativas de diseño:– ¿Se permite prueba (dinámica) de tipo?
• Requiere uso de un discriminador o marca de tipo
– ¿Se les puede incrustar en un registro?
37
Ejemplo: C y C++
union direccion {char dominio[20];int IP[4];
};
Dominio
IP
Comentarios sobre Uniones
• Reserva espacio de memoria igual al mayor miembro definido.
• Todos los miembros comparten la memoria y comienzan desde la misma dirección.
• Su uso es en general poco seguro (e.g. Pascal, C, C++, Fortran y Modula-2), lo que hace que muchos lenguajes no sean de tipificación fuerte.
• Java no provee este tipo de estructura
38
Ejemplo: Uniones en Pascal• Sin discriminador:
• Con discriminador:TYPE
coordenada_t = (cartesiana, polar);complejo_t =
RECORD CASE coord : coordinada_t OF Cartesiana: (x, y : real);
polar: (r, o : real); END;
TYPEcoordenada_t = (cartesiana, polar);complejo_t =
RECORD CASE coordinada_t OF Cartesiana: (x, y : real);
polar: (r, o : real); END;
Aplicación del Discriminador
VAR u, v : complejo_t;
BEGIN…
{ copiar u a v en coordenadas polares }
v.coord := polar; CASE (u.coord) OF
polar: BEGIN v.r := u.r; v.o := u.o END;cartesiana : BEGIN
v.r := sqrt(sqr(u.x)+sqr(u.y); v.o := arctan(u.y/u.x)
END END;
39
Ejemplo: Uniones en C y C++
typedef enum {CARTESIANO, POLAR} coordenada_t;struct complejo_t {
coordenada_t tipo_coord;union {
struct{real rad,ang;} p;struct{real x,y;} c;
} tag;} u,v;
#define polar tag.p#define cartesiano tag.c
if (u.tipo_coord == POLAR) {u.tag.p.ang = PI/2; // mas engorrosou.polar.rad = 34.7; // mejor lectura
}
Tipo Conjunto
• Permite almacenar un conjunto no ordenado de elementos de un tipo ordinal: tipo base.
• Sólo disponible en Pascal y Modula-2• El tamaño del tipo base es dependiente de la
implementación– La razón es que su implementación se realiza
mediante un arreglo de bits.– Limita su uso a pequeños conjuntos ( < 100)
40
Ejemplo: Pascal
• Operadores: […], +, *, >=, <=, IN, []
TYPEcaracter = SET OF char;
VAR vocal, letras, no_vocal: caracter;c : char;
BEGIN vocal := [´A´,´E´,´I´,´O´,´U´] + [´a´,´e´,´i´,´o´,´u´]; letras := [´A´..´Z´] + [´a´..´z´]; no_vocal := letras – vocal; …
IF (c IN vocal) THENBEGIN { procesar vocal} … END;
ELSE …
2.3.4 Tipo Puntero
Operaciones, problemas de uso, tipo referencia, implementación y gestión
del heap
41
Tipo Puntero• Su valor corresponde a una dirección de memoria,
habiendo un valor especial nulo (nil) que no apunta a nada.
• Aplicaciones:– �Método de gestión dinámica de memoria
(acceso a variables dinámicas de heap)– �Método de direccionamiemto indirecto
• No corresponde a un tipo estructurado, aun cuando se definen en base a un operador de tipo
Operaciones con Punteros
• Asignación: asigna a la variable como valor una dirección a algún objeto de memoria.
• Desreferenciación: entrega el valor del objeto apuntado.– Operador * en C y C++ (e.g. *ptr)– Operador ^ en Pascal (e.g. ptr^)
42
Ejemplo en C
int j, *ptr;
ptr= (int*) malloc(sizeof(int));*ptr = 432;j = *ptr;
432432
ptr2145
7023
j7127
Problema con Punteros
• � Dejar colgado (dangling)typedef tipo* ptipo;tipo x;ptipo p, q;
p= (ptipo) malloc(sizeof(tipo));...q= p;...free(p);... /* puede que la variable de heap sea reasignada */*q = x;
43
Problema con Punteros
• �Pérdida de variables dinámicas de heap
• Se pierde el acceso a la variable, convirtiéndose en basura dado que no puede ser reasignada
tipo* p;
p= (tipo *) malloc(sizeof(tipo));...p= (ptipo *) malloc(sizeof(tipo));/* se pierde la variable asignada anteriormente */
Ejemplo de Punteros en Pascal
TYPE enlace_t = ^nodo_t; nodo_t = RECORD info : tipo_dato; siguiente: enlace_t END;VAR lista, nodo: enlace_t;
BEGIN... new(nodo); nodo^.info := dato; nodo^.siguiente := lista; lista := nodo;
44
Punteros y Estructuras en C y C++struct nodo_t { tipodato info; struct nodo_t *siguiente; }; typedef nodo_t* enlace_t;
enlace_t nodo;
#ifndef C++ nodo = (enlace_t) malloc(sizeof(nodo_t));#elif nodo = new nodo_t; /* caso C++ */#endif (*nodo).info = dato;
/* forma más conveniente de referirse es */ nodo->siguiente = NULL;
Autoreferencia
Punteros y Arreglos en C y C++
• Un arreglo es en realidad una constante de tipo punteroint a[10];int *pa;
pa = &a[0];pa = a; /* hace lo mismo que la linea anterior */
for (int i=0; i<10; i++) printf(a[i]);for (int i=0; i<10; i++) printf(*(pa+i));for (int i=0; i<10; i++) printf(*(a+i));/* los tres for hacen lo mismo */
45
Aritmética de Punteros en C y C++#define ALLOCSIZE 1000 /* tamaño del buffer */ static char allocbuf[ALLOCSIZE]; /* el buffer */ static char* allocp = allocbuf; /* primera posición libre */ char *alloc(int n) / retorna puntero a “n” caracteres */ {
if (allocbuf + ALLOCSIZE – allocp >= n) { /*si cabe */ allocp += n; return allocp – n; /* antigua direccion */ } else return 0; }
void afree (char *p) {
if (p >= allocbuf && p < allocbuf + ALLOCSIZE) allocp = p; }
Tipo Referencia en C++
• Un tipo referencia es una variable tipo puntero constante que es implícitamente desreferenciada.
• Dado que es constante, debe ser inicializada en su definición.
• La referencia actúa como alias de una variable.
int valor = 3;int &ref_valor = valor;
ref_valor = 100; /* ahora valor == 100 */
46
Referencias en Funciones de C++• Su uso en parámetros de funciones permite pasada
por referencia (comunicación bidireccional).• Inicialización se produce en el momento de la
invocación.void swap(int &a, int &b){
int tmp = a;a = b;b = tmp;
}...int a, b;...swap(a,b);...
void swap(int *a, int *b){
int tmp = *a;*a = *b;*b = tmp;
}...int a, b;...swap(&a,&b);...
Referencias en Java
• Java extiende la forma de variables de referencia de C++, haciendo innecesario y, por lo tanto, eliminando el uso de punteros. – Java permite asignar un nuevo objeto a una
variable de referencia– Todo objeto sólo se referencia con estas
variables• En Java liberación de objetos es implícita
47
Técnicas para evitar Dangling
• Lápida sepulcral (tombstone)• Enfoque de Llave y Claves (locks-and-keys)• No existe liberación explícita (e.g. Java)
� Método de la Lápida Sepulcral(Tombstone)
Heap
TombstonesVariablesDinámicas
48
� Método de Llave y Clave(Locks-and-Keys)
Heap
lock data
key1 address
key2 address
key1 = lockkey2 ≠ lock
Gestión del Heap
• Situación más simple es administrar objetos de memoria (o celda) de un tamaño único– Celdas libres se pueden enlazar con punteros en una
lista– Asignación es simplemente tomar suficientes celdas
(contiguas) de la lista anterior– Liberación es un proceso más complicado
(se debiera evitar dangling y basura).• Tamaño variable es lo normalmente requerido por
los lenguajes, pero es complejo de implementar
49
Casos para una Celda de Heap
• Libre: no tiene referencias y está marcada libre• Ocupada: tiene al menos una referencia y está
asignada (no está marcada como libre)• Basura: No tiene referencia y no está marcada
como libre ∴ el administrador no la puede reasignar
• Dangling: tiene alguna referencia y está marcada como libre
∴ el administrador la podría reasignar
Métodos de Reclamo de Basura• Contadores de Referencia (impaciente)
– Se mantiene un contador de referencia por cada celda– Se incrementa con una nueva referencia y se
decrementa cuando se pierde una referencia– Celda se libera tan pronto cuenta llega a cero
(cuando se convierte en basura)• Recolección de Basura (perezoso)
– Se acumula basura hasta que se agota la memoria– Si se agota la memoria se identifican las celdas de
basura y se pasan a la lista de celdas libres
50
Recolección de Basuravoid* allocate (int n){
if (!hay_espacio) { /* recolectar basura */ 1) marcar todo los objetos del heap como basura; 2) for (todo puntero p) {
if (p alcanza objeto o en el heap) marcar o como NO basura; } /* for */ 3) liberar todos los objetos marcados como basura }
if (hay_espacio) { asignar espacio; return puntero al objeto; } else return NULL;}
Evaluación de los Métodos
• Contadores de referencia– Requiere bastante memoria para mantener
contadores– Asignaciones a punteros requiere de más
tiempo de ejecución para mantener contadores• Recolección de basura
– Basta un bit por celda para marcar basura– Mal desempeño cuando queda poca memoria
51
Celdas de Tamaño Variable
• Mayor parte de los lenguajes requieren variables de tamaño variable.
• Mantención de celdas asignadas y libres se hace más difícil y costosa.
• Se requiere más memoria para mantener información sobre tamaño, estado, etc.
• Se produce fragmentación de la memoria.
2.4 Expresiones y Asignaciones
Expresiones aritméticas y lógicas, sobrecarga de operadores,
conversiones de tipo, evaluación con corto-circuito
52
Introducción
• Lenguajes imperativos se caracterizan por el uso dominante de expresiones y asignaciones
• El valor de las expresiones depende del orden de evaluación de operadores y operandos
• Ambigüedades en el orden de la evaluación puede conducir a diferentes resultados
Expresiones Aritméticas
• Orden de evaluación está principalmente definida por las reglas de precedencia y asociatividad
• Paréntesis fuerzan determinado orden• Representación finita de números puede
tener efectos no deseados en el orden de la asociatividad
53
Reglas de Precedencia de Operadores Aritméticos
Fortran Pascal C** *, /, div, mod Postfix ++, --*, / +, - Prefix ++, --+, - +, - (unario)
*, /, %+, - (binario)
Alta
Baja
Reglas de AsociatividadFORTRAN Izq: *, /, +, -
Der: **Pascal Todos por la izquierda
C Izq: ++ y –– postfijo; *, /, %, + y –binario
Der: ++ y –– prefijo; + y – unariosC++ Izq: *, /, %, + y - binario
Der: ++, --, - y + unario
54
Expresión Condicional/* version N°1 */ int abs(int n) {
if (n>=0) return n; else return –n; }
/* version N°2 */ int abs(int n) {
return (n>=0)? n: -n; }
Efectos Laterales Funcionales• Orden de evaluación de los operandos puede
producir resultados diferentes si existen efectos laterales
• Ejemplo: int a = 2; int f1() { return a++; }
int f2 (int i) { return (--a * i); }
void main { printf(“%i\n”, f1()*f2(3)); }
55
¿Cómo evitar efectos laterales de funciones?
• Deshabilitar efectos laterales en la evaluación de funciones– Quita flexibilidad, por ejemplo habría que negar
acceso a variables globales• Imponer un orden de evaluación a las funciones
– Evita que el compilador puede realizar optimizaciones– Enfoque seguido en Java (evaluación de izquierda a
derecha).
Sobrecarga de Operadores
• Un mismo operadores puede ser usado con diferentes tipos de operandos y para diferentes fines.
• Ejemplo: + para sumar enteros y reales (tb. strings)& en C es AND al bit y operador de dirección
• Ayuda a mejorar la lectura, pero puede que errores de escritura no sean detectados.
56
Sobrecarga de Operadores definidas por el Programador
• Algunos lenguajes con soporte de TDA permiten al programador sobrecargar símbolos de operadores (e.g. ADA, Fortran 90 y C++)
matrix A, B, C, D;
D = A + (B*C); /* es mas conveniente, pero requiere de sobrecarga de operadores; sino debiera escribirse en un estilo como: */ MatrixAssign(D, MatrixAdd(A, MatrixMult(B, C)));
Conversiones de Tipo• Clases: extensión o estrangulamiento
– Extensión: paso de entero a punto flotante o subrango de enteros a entero
– Estrangulamiento: paso de real a entero, de tipo base a subrango en general
• En general, extensión es más segura, pero puede tener algunos problemas (e.g. pérdida de precisión en la mantisa de entero a real)
57
Coerción en Expresiones
• Coerción es cuando la conversión de tipo es implícitamente asumida por el compilador.
• Da más flexibilidad al uso de operadores, pero reduce la posibilidad de detectar errores y introduce código adicional.
• Ada y Modula-2 admiten pocos casos de coerción• Java enteros cortos (byte, short y char) se
convierten a int.• Conversión explícita se denomina casting
Expresiones Relacionales
Operación Pascal C Ada Fortran
Igual = == = .EQ.
No es igual <> != /= .NE.
Mayor que > > > .GT.
Menor que < < < .LT.
Mayor oigual que
>= >= >= .GE.
Menor oigual que
<= <= <= .LE.
58
Operadores Booleanos
• Incluye: AND, OR, NOT y, a veces, XOR.• La precedencia está generalmente definida de
mayor a menor: NOT, AND y OR (excepto ADA, que todos tienen igual sin considerar NOT).
• Operadores aritméticos tienen mayor precedencia que relacionales y , generalmente, booleanosmenor que relacionales (excepto Pascal).
• La asignación en C, C++ y Java es el operador que tiene la menor precedencia
Ejemplos:Operadores relacionales y lógicos
C: b + 1 > b*2 /* aritmeticos primero */
C: a > 0 || a < 5 /* relacional primero */
Pascal: a > 0 OR a < 5 {es ilegal}
59
Corto Circuito o Término Anticipado de EvaluaciónC:
(13*a) * (b/13 –1)
(a >=0) || (b < 10)
while ( (c = getchar()) != EOF && c != ´\n´) { procesar linea;
PASCAL:
i := 1; WHILE (i <= listlen) AND (list[i] <> key) DO
i := i+1;
Corto Circuitos en los Lenguajes Imperativos
• C, C++ y Java definen corto circuito para:&& y || (AND y OR)
• Modula-2 tb. lo define para: AND y OR• Pascal no lo especifica (algunas
implementaciones los tienen, otras no); algo parecido sucede con Fortran
• ADA permite especificar explícitamente con los operadores: and then y or else
60
Sentencias de Asignación
• Permite cambiar dinámicamente el valor ligado a una variable
• Fortran, Basic, PL/I, C, C++ y Java usan =• Algol, Pascal y ADA usan :=• C, C++ y Java permiten incrustar una
asignación en una expresión (actúa como cualquier operador binario)
Asignación Múltiple
• PL/I permite:SUM, TOTAL = 0
• C, C++ y Java permiten:SUM = TOTAL = 0
61
Asignación Condicional
• C, C++ y Java permiten:(a > 0) ? cuenta1: cuenta2 = 0;
• Equivale a:if (a > 0)cuenta1 = 0;else cuenta2 = 0;
Operadores Compuestos y Unariosde Asignación
• Sólo presentes en C, C++ y Java
sum += A[i]; equivale a: sum = sum + A[i];
sum = ++contador; equivale a: contador = contador + 1; sum = contador;
62
Asignación en Expresiones
• C, C++ y Java permiten incrustar asignaciones en cualquier expresión
• Permite codificar en forma más compacta• La desventaja de esta facilidad es que puede
provocar efectos laterales, siendo fuente de error en la programación
• Es fácil equivocarse confundiendo == y =
Coerción en la Asignación
• Habilitar asignación en modo mixto requiere de reglas de conversión de tipo
• C, C++ y Fortran permiten aplicación libre de coerción, aumentando riesgo de errores
• Pascal limita coerción, por ejemplo entero a real, pero no viceversa
• Java se diferencia de C y C++ permitiendo sólo coerción en extensión.
63
2.5 Estructuras de Control
Expresiones aritméticas y lógicas, sobrecarga de operadores,
conversiones de tipo, evaluación con corto-circuito
Introducción
• Ejecución es típicamente secuencial• Normalmente se requieren dos tipos de
sentencias de control:– Selección de alternativas de ejecución– Ejecución repetitiva de un grupo de sentencias
• En principio basta tener un simple gotoselectivo, pero es poco estructurado
64
1) Sentencias Compuestas
• Permite agrupar un conjunto de sentencias• Ejemplos:
– begin y end en Pascal y Algol– Paréntesis de llave en C, C++ y Java
2) Sentencias de Selección
• Selección binariaEjemplo: if-else o if (solo)
• Selección múltipleEjemplos: – case en Pascal– switch en C, C++ y Java– elseif en ADA
65
3) Sentencias Iterativas
• Bucles controlados por contadorEjemplo: for en C
• Bucles controlados por condiciónEjemplo:– while, do-while en C, C++ y Java– loop-exit en ADA
• Bucles controlados por Estructuras de DatosEjemplo: foreach en Perl
4) Salto Incondicional
• Uso de rótulos o etiquetas– Ejemplo: goto FINAL
• Restricciones:– Sólo rótulos en el ámbito de la variable– Rótulos deben ser constantes
66
5) Guarded Command
• Ejemplos en CSP
if exp1 -> sentencia1[] exp2 -> sentencia2...[] expn -> sentencianfi
do exp1 -> sentencia1[] exp2 -> sentencia2...[] exp2 -> sentencia2od
2.6 Subprogramas
Ámbito, comprobación de tipos, semántica de paso de parámetros,
funciones genéricas, implementación
67
Características de un Subprograma
• Permite crear abstracción de proceso:– encapsulando código– definiendo una interfaz de invocación para paso
de parámetros y resultados• Permite reutilizar código, ahorrando
memoria y tiempo de codificación.• Existe en forma de procedimiento y
función.
Mecanismo de Invocación
Subprograma
Parámetros
Resultados
Activación
Invocación y
suspensión
RetornoReanu-dación
68
Elementos en la Definición de Interfaces de Subprogramas
• Nombre: permite referenciar al subprograma como unidad e invocarlo.
• Parámetros (Opcional): Define la comunicación de datos (nombre, orden y tipo de parámetros formales):
• Valor de retorno: Opcional para funciones (tipificado).
• Excepciones (Opcional): Permite manejo de un evento de excepción al retornar el control.
Firmas y Protocolos de la Interfaz
• La firma (signature), o prototipo, es un contrato entre el invocador y el subprograma que define la semántica de la interfaz.
• El protocolo especifica cómo debe realizarse la comunicación de parámetros y resultados (tipo y orden de los parámetros y, opcionalmente, valor de retorno).
procedure random(in real semilla; out real aleat);
69
Parámetros
• Parámetros formales son variables mudas que se ligan a los parámetros reales cuando se activa el subprograma.– Normalmente ligado se hace según posición en la lista.
• Parámetros permiten comunicación explícita de datos y, a veces, también (otros) subprogramas.
• Comunicación implícita se da a través de variables no locales, lo que puede provocar efectos laterales.
Subprogramas en Cy C++
int notas[50]; ... void sort (int lista[], int largo); ... sort(notas, 50);
float potencia(float base, float exp); calculo = x * potencia(y, 2.5);
70
Variables Locales Estáticas y Dinámicas de Stack
Característica Estática DinámicaOverhead deAsignación
Sin costo Con costo
Acceso Directo(rápido)
Indirecto(lento)
Memoria No comparte ComparteHistoria sensible insensibleRecursión No soporta Si soporta
Semántica de Paso de Parámetros
• Modo de interacción de parámetro actual a formal puede ser: – entrega de valor (IN)– recibo de valor (OUT)– ambos (INOUT)
• La implementación de la transferencia de datospuede ser:– copiando valores, o– pasando referencias (o puntero)
71
� Paso por Valor(pass-by-value)
• Modo IN e implementado normalmente con copia de valor– Implementación con paso de referencia requiere
protección de escritura, que puede ser difícil
• Permite proteger de modificaciones al parámetro actual, pero es más costoso – (más memoria y tiempo de copiado)
• Permite usar expresiones como parámetro actual
� Paso por Resultado(pass-by-result)
• Modo OUT y normalmente implementado con copia (mismas complicaciones que por valor)
• Parámetro formal actúa como variable local, pero al retornar copia valor a parámetro actual
• Parámetro actual debe ser variable• Dificultades:
– Existencia de colisiones en los parámetros actuales, puede conducir a ambigüedad
– Cuándo se evalúa dirección de parámetro actual?
72
� Paso por Valor-Resultado(pass-by-value-result)
• Modo INOUT con copia de parámetros en la entrega y en el retorno– Por esto, llamada a veces paso por copia
• Mismas dificultades que paso por valor y paso por resultado
� Paso por Referencia(pass-by-reference)
• Modo INOUT e implementación con referencias• Parámetro formal y real comparten misma variable• Ventaja: Comunicación es eficiente en:
– espacio (no requiere duplicar variable)– tiempo (no requiere copiar)
• Desventaja– Acceso es más lento (indirección)– Es fuente de error (modificación de parámetro real)– Creación de alias a través de parámetros actuales
73
� Paso por Nombre(pass-by-name)
• Modo INOUT, pero diferente a los modelos anteriores
• Nombres del parámetro real se liga al parámetro formal en el momento de la activación, pero valor o dirección se liga en el momento de la referencia
• Es muy flexible, pero costoso (lento) y difícil de implementar y entender
• Usado en ALGOL 60, discontinuándolo en versiones sucesivas
Ejemplo 1:Paso por Referencia vs. Paso por Valor-Resultado
procedure EJEMPLO1;integer X;procedure SUB(inout integer PARAM)
beginX := 2;PARAM := PARAM +1;
end;begin {EJEMPLO1}
X := 1;SUB(X);
end;
Por Referencia:X = 3
Por Valor-Resultado:X = 2
74
Ejemplo 2:Paso por Referencia vs. Paso por Nombreprocedure EJEMPLO2;
integer INDICE;integer array LISTA[1.2];
procedure SUB(PARAM)integer PARAM;begin
PARAM := 4;INDICE := INDICE + 1;PARAM := 5;
end; {SUB}
begin {EJEMPLO2}INDICE := 1;LISTA[1] := 2;LISTA[2] := 3;SUB(LISTA[INDICE]);
end;
Por Referencia:LISTA = [5,3]
Por Nombre:LISTA = [4,5]
Paso de Parámetros en C y C++
• C: Paso por valor, y por referencia usando punteros (parámetros deben ser desreferenciados).– Puntero pueden ser calificado con const; se logra
semántica de paso por valor (sin permitir asignación)– Arreglos se pasan por referencia (son punteros)
• C++: Igual que C, más paso por referencia usando operador & (sin necesidad de desreferenciar).– Este operador también puede ser calificado con const,
permitiendo semántica paso por valor con mayor eficiencia (e.g. paso de grandes arreglos)
75
Paso de Parámetros en otros Lenguajes Populares
• Java: Todos los parámetros son pasados por valor, excepto objetos que se pasan por referencia– No existencia de punteros no permite paso por
referencia de escalares (si como parte de un objeto)
• Pascal y Modula-2: Por defecto paso por valor, y por referencia si se usa calificativo var.
• ADA: Por defecto paso por valor, pero todos los parámetros se pueden calificar con in, out yinout.
Comprobación de Tipos de Parámetros
• Tendencia es a realizar comprobación de tipos, lo cual permite detectar errores.
• Pascal, Modula-2, Fortran 90, Java y ADA lo requieren.
• C en su primera versión no lo requiere, pero a partir de ANSI C si (método de prototipo).– Ante incompatibilidad de tipos el compilador
realiza coerción, si es posible
76
Comprobación de Tipos de Parámetros en C++
• Todas las funciones deben usar la forma de prototipo (ANSI C lo adoptó de C++)
• Sin embargo se puede desactivar mediante una elipsis
double power (double base, float exp);
int printf(const char* ...);
int file_print(FILE*, char*, ...);
Implementación de los Métodos de Paso de Parámetro
• Comunicación de parámetro se realiza mediante el stack– Por valor: Al invocar, valor de la variable se copia al
stack.– Por resultado: Al retornar, valor se copia del stack a la
variable.– Por valor-resultado: combinando las anteriores.– Por referencia: Se escribe la dirección en el stack y
luego se usa direccionamiento indirecto (el más simple de implementar).
77
Funciones como Parámetro
• En Pascal
• En C (solo usando punteros a funciones)
function integrar (function fun(x:real): real; bajo, alto: real) : real;...x := integrar(coseno, -PI/2, PI/2);...
float integrar (float *fun(float), float bajo, float alto);...x = integrar(&coseno, -PI/2, PI/2);...
Sobrecarga de Subprogramas
• En el mismo ámbito existen diferentes subprogramas con el mismo nombre.
• Cada versión debiera tener una firma diferente, de manera que a partir de los parámetros reales se pueda resolver a cual versión se refiere.
• Las versiones pueden diferir en la codificación• Es una conveniencia notacional, que es evidente
cuando se usan nombres convencionales, como en siguiente ejemplo
78
Ejemplo: Sobrecarga de Funciones en C++
double abs(double); int abs(int); abs(1); // invoca int abs(int); abs(1.0); // invoca double abs(double);
// se sobrecargará print void print(int); void print (char*);
Subprogramas Genéricos• Permite crear diferentes subprogramas que
implementan el mismo algoritmo, el cual actúa sobre diferentes tipos de datos.
• Mejora la reutilización, aumentando productividad en el proceso de desarrollo de software.
• Polimorfismo paramétrico: Parámetros genéricos de tipos usados para especificar los tipos de los parámetros de un subprograma
• Sobrecarga de subprogramas corresponde a un polimorfismo ad-hoc
79
Funciones Genéricas en C++
template <class Tipo> Tipo maximo (Tipo a, Tipo b) {
return a>b ? a : b; }
int x, y, z; char u, v, w; z = maximo(x, y); w = maximo(u, v);
Sobrecarga de Operadores definida por el Usuario
• Permite que el usuario sobrecargue operadores existentes en el lenguaje (Lo permiten ADA y C++)
int operator * (const vector &a, const vector &b, int len)
{ int sum = 0; for (int i = 0; i < len; i++) sum += a[i] * b[i]; return sum; }... vector x, y; ... printf(“%i”, x * y);
80
Compilación Separada e Independiente
• Compilación Separada: unidades de programas pueden compilarse en diferentes tiempos, pero se consideran dependencias (tal como comprobación de interfaces, variables, etc.) de acuerdo a lo que exporta e importa (e.g. ADA)
• Compilación Independiente: Se compilan unidades de programa sin información de otras (e.g. C, C++ y Fortran)
• Es mejor compilación separada, pero es mejor tener compilación independiente que no tener nada