Lenguajes de Programación I
Parcial 2022
Ejercicio 3 – Semántica Lenguaje Color
Dado el siguiente programa en el lenguaje Color del ejercicio anterior, sin errores de compilación, que se ejecuta correctamente:
|
1 2 3 4 5 6 7 8 9 1011 12 13 14 15 16 17 18 19 20 21 22 23 | negro int a=0; negro int i_loop=0; negro double c= 2.5; int RO(rojo int ro){ print (“La dirección de ro es: %d y su valor es %d“,&ro, ro); ro<-1000; } int AZ(azul int az){ print (“La dirección de az es: %d y su valor es %d“,&az, az); az<-2000; } int VI(violeta int vi){ print (“La dirección de vi es: %d y su valor es %d“,&vi, vi); vi<-3000; } int VE(verde int ve){ print (“La dirección de ve es: %d y su valor es %d“,&ve, ve); ve<-4000; } int AM(amarillo int am){ print (“La dirección de am es: %d y su valor es %d“,&am, am); am<-5000; } |
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | int primera(){ int a=25; blanco int[] numeros= [1, 2, 3, 4, 5]; print (“La dirección de numeros[0] es %d“,&numeros[0]); numeros <- [10, 25, 40, 55, 75, 100, 112, 126, 128]; print (“La dirección de numeros[0] es %d“,&numeros[0]); print (“La dirección de a es %d“,&a); RO(a); print (“El valor de a es %d“,a); AZ(a); print (“El valor de a es %d“,a); VI(a); print (“El valor de a es %d“,a); VE(a); print (“El valor de a es %d“,a); AM(a); print (“El valor de a es %d“,a); } |
42 43 44 45 46 47 48 49 50 | float recursiva(amarillo int b5){ negro double x=b5; x<-b5+c; print (“La dirección de x es: %d“,&x); while(i_loop<3){ i_loop<-i_loop+1; recursiva(b5); } } |
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | int main (){ int b=10; double d; print(“Direcciones: a=%d b=%d c=%d d=%d”, &a, &b, &c, &d); primera(); recursiva(b); b:=a; a<-8; if (b==a) { print(“a y b son iguales”); } else { print(“a y b son distintas”); } d<-c; c<-7.9; if (c==d) { print(“c y d son iguales”); } else { print(“c y d son distintas”); } } |
SALIDA |
Direcciones: a=-5000 b=300 c=-5004 d=304 La dirección de numeros[0] es 1284300 La dirección de numeros[0] es 1284314 La dirección de a es 318 La dirección de ro es: 330 y su valor es 25 El valor de a es 25 La dirección de az es: -5000 y su valor es 0 El valor de a es 25 La dirección de vi es: 330 y su valor es 4941284 El valor de a es 3000 La dirección de ve es: 318 y su valor es 3000 El valor de a es 4000 La dirección de am es: 330 y su valor es 4000 El valor de a es 5000 La dirección de x es: -5008 La dirección de x es: -5008 La dirección de x es: -5008 La dirección de x es: -5008 a y b son iguales c y d son distintas |
Elemento de la gramática | Semántica |
negro | |
blanco | |
rojo | |
azul | |
violeta | |
verde | |
amarillo | |
:= | |
<- | |
4. ?declaracionvar: almacenamiento tipo id | almacenamiento tipo id "=" expresion | tipo id | tipo id "=" expresion
5. ?almacenamiento : "negro" | "blanco"
1 2 3 | negro int a=0; negro int i_loop=0; negro double c= 2.5; |
24 25 26 27 28 29 30 | int primera(){ int a=25; blanco int[] numeros= [1, 2, 3, 4, 5]; print (“La dirección de numeros[0] es %d“,&numeros[0]); numeros <- [10, 25, 40, 55, 75, 100, 112, 126, 128]; print (“La dirección de numeros[0] es %d“,&numeros[0]); … } |
42 43 44 45 46 47 48 49 50 | float recursiva(amarillo int b5){ negro double x=b5; x<-b5+c; print (“La dirección de x es: %d“,&x); while(i_loop<3){ i_loop<-i_loop+1; recursiva(b5); } } |
main->primera-> COLORES -> recursiva->recursiva->recursiva->recursiva
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | int main (){ int b=10; double d; print(“Direcciones: a=%d b=%d c=%d d=%d”, &a, &b, &c, &d); primera(); recursiva(b); b:=a; a<-8; if (b==a) { print(“a y b son iguales”); } else { print(“a y b son distintas”); } d<-c; c<-7.9; if (c==d) { print(“c y d son iguales”); } else { print(“c y d son distintas”); } } |
main
b,d
recursiva
negro x
global
negro: a, i_loop, c
primera
a, blanco numeros
SALIDA |
Direcciones: a=-5000 b=300 c=-5004 d=304 La dirección de numeros[0] es 1284300 La dirección de numeros[0] es 1284314 La dirección de a es 318 La dirección de ro es: 330 y su valor es 25 El valor de a es 25 La dirección de az es: -5000 y su valor es 0 El valor de a es 25 La dirección de vi es: 330 y su valor es 4941284 El valor de a es 3000 La dirección de ve es: 318 y su valor es 3000 El valor de a es 4000 La dirección de am es: 330 y su valor es 4000 El valor de a es 5000 La dirección de x es: -5008 La dirección de x es: -5008 La dirección de x es: -5008 La dirección de x es: -5008 a y b son iguales c y d son distintas |
a(global) -5000
c(global) -5004
x(recursiva) -5008
main
b,d
recursiva
negro x
global
negro: a, i_loop, c
primera
a, blanco numeros
numeros[0] (primera)
1284300
numeros[0] (primera)
1284314
b(main)
300
d(main)
304
a(primera)
318
param. formal(RO, VI, AM)
330
a(global) -5000
c(global) -5004
x(recursiva) -5008
main
b,d
recursiva
negro x
global
negro: a, i_loop, c
primera
a, blanco numeros
numeros[0] (primera)
1284300
numeros[0] (primera)
1284314
b(main)
300
d(main)
304
a(primera)
318
param. formal(RO, VI, AM)
330
negro: están en la misma sección a pesar de haber sido declaradas en ámbitos distintos
SALIDA |
La dirección de x es: -5008 La dirección de x es: -5008 La dirección de x es: -5008 La dirección de x es: -5008 |
42 43 44 45 46 47 48 49 50 | float recursiva(amarillo int b5){ negro double x=b5; x<-b5+c; print (“La dirección de x es: %d“,&x); while(i_loop<3){ i_loop<-i_loop+1; recursiva(b5); } } |
2 | negro int i_loop=0; |
main
recursiva
recursiva
recursiva
recursiva
Si existiese una x por cada invocación de recursiva, no tendría siempre la misma dirección
Elemento de la gramática | Semántica |
negro | almacenamiento estático: -a, c y x se encuentran en la misma sección de memoria a pesar de haber sido declaradas en ámbitos distintos -x se encuentra una única vez en memoria a pesar de existir cuatro invocaciones recursivas de la función que la contiene |
Respuesta completa:
Justificaciones incompletas:
“se encuentran en direcciones -500X”
“se encuentran muy abajo en la pila”
24 25 26 27 28 29 30 | int primera(){ int a=25; blanco int[] numeros= [1, 2, 3, 4, 5]; print (“La dirección de numeros[0] es %d“,&numeros[0]); numeros <- [10, 25, 40, 55, 75, 100, 112, 126, 128]; print (“La dirección de numeros[0] es %d“,&numeros[0]); … } |
numeros[0] (primera)
1284300
numeros[0] (primera)
1284314
números cambia de tamaño en la misma ejecución de primera
-El RA no puede cambiar de tamaño, por lo que no se encuentra dentro del RA.
-Las direcciones muestran que los datos de números se encuentran en una sección distinta de memoria
-El cambio de dirección del elemento 0 del arreglo indica que el arreglo se alojó en otro espacio de memoria al cambiar de tamaño.
Es el comportamiento de una variable con almacenamiento dinámico (con nombre)
Elemento de la gramática | Semántica |
blanco | almacenamiento dinámico (con nombre): -la variable numeros cambió de tamaño en la misma ejecución de primera -la variable numeros se movió en memoria (en el heap) en la misma ejecución de primera |
Respuesta completa:
Justificaciones incompletas:
“se encuentran en direcciones 1284XXX”
“se encuentran muy arriba en la pila (heap)”
4 5 6 7 8 9 1011 12 13 14 15 16 17 18 19 20 21 22 23 | int RO(rojo int ro){ print (“La dirección de ro es: %d y su valor es %d“,&ro, ro); ro<-1000; } int AZ(azul int az){ print (“La dirección de az es: %d y su valor es %d“,&az, az); az<-2000; } int VI(violeta int vi){ print (“La dirección de vi es: %d y su valor es %d“,&vi, vi); vi<-3000; } int VE(verde int ve){ print (“La dirección de ve es: %d y su valor es %d“,&ve, ve); ve<-4000; } int AM(amarillo int am){ print (“La dirección de am es: %d y su valor es %d“,&am, am); am<-5000; } |
Cada función imprime dirección y valor de parámetro formal y le cambia el valor
24 25 … 30 31 32 33 34 35 36 37 38 39 40 41 | int primera(){ int a=25; … print (“La dirección de a es RO(a); print (“El valor de a es %d“,a); AZ(a); print (“El valor de a es %d“,a); VI(a); print (“El valor de a es %d“,a); VE(a); print (“El valor de a es %d“,a); AM(a); print (“El valor de a es %d“,a); } |
Se llama a cada función con a como parámetro real.
Al volver, se imprime el valor de a local. Esto nos va a servir para ver si fue modificada
a(primera)
318
|
RO VI y AM almacenan el parámetro formal (alguna variante de copia)
AZ no almacena parámetro formal (nombre usando reemplazo textual)
VE tiene un puntero (referencia usando punteros)
4 5 6 7 | int RO(rojo int ro){ print (“La dirección de ro es: %d y su valor es %d“,&ro, ro); ro<-1000; } |
a(primera)
318 (valor 25)
SALIDA |
La dirección de ro es: 330 y su valor es 25 El valor de a es 25 |
Se copió el valor al llamar a la función
El parámetro formal existe en memoria
No se copió el valor al finalizar la ejecución de la función
COPIA VALOR
8 9 10 11 | int AZ(azul int az){ print (“La dirección de az es: %d y su valor es %d“,&az, az); az<-2000; } |
a(primera)
318 (valor 25)
SALIDA |
La dirección de az es: -5000 y su valor es 0 El valor de a es 25 |
El parámetro formal no existe en memoria (se está usando a global)
NOMBRE
1 | negro int a=0; |
a(global) -5000
Al finalizar la ejecución, la a local de primera() no se modificó
12 13 14 15 | int VI(violeta int vi){ print (“La dirección de vi es: %d y su valor es %d“,&vi, vi); vi<-3000; } |
a(primera)
318 (valor 25)
SALIDA |
La dirección de vi es: 330 y su valor es 4941284 El valor de a es 3000 |
No se copió el valor al llamar a la función (se lee un valor basura)
El parámetro formal existe en memoria
Se copió el valor al finalizar la ejecución de la función
COPIA RESULTADO
16 17 18 19 | int VE(verde int ve){ print (“La dirección de ve es: %d y su valor es %d“,&ve, ve); ve<-4000; } |
a(primera)
318 (valor 3000)
SALIDA |
La dirección de ve es: 318 y su valor es 3000 El valor de a es 4000 |
Se lee el valor del parámetro real
El parámetro formal está referenciando al real
Se modificó directamente el valor del parámetro real
REFERENCIA
20 21 22 23 | int AM(amarillo int am){ print (“La dirección de am es: %d y su valor es %d“,&am, am); am<-5000; } |
a(primera)
318 (valor 4000)
SALIDA |
La dirección de am es: 330 y su valor es 4000 El valor de a es 5000 |
Se copió el valor al llamar a la función
El parámetro formal existe en memoria
Se copió el valor al finalizar la ejecución de la función
COPIA VALOR RESULTADO
51 52 53 … 57 58 59 60 61 62 63 64 65 | int main (){ int b=10; double d; … b:=a; a<-8; if (b==a) { print(“a y b son iguales”); } else { print(“a y b son distintas”); } d<-c; c<-7.9; if (c==d) { print(“c y d son iguales”); } else { print(“c y d son distintas”); } } |
1 3 | negro int a=0; negro double c= 2.5; |
Al cambiar el valor de a, se cambia también el de b. Ambos nombres de variable quedaron ligados a la misma celda de memoria -> Asignación por referencia
Al cambiar el valor de c, no se modifica el de d. Ambos nombres de variable son independientes -> Asignación por copia
b) Indique si cada afirmación es verdadera o falsa, justificando todas sus respuestas. En caso de ser verdadera, justificar indicando en qué líneas del programa se denotan las características indicadas:
negro int a=0; int AZ(azul int az){ print (“La dirección de az es: %d y su valor es %d“,&a, a); az<-2000; } int primera(){ int a=25; … print (“La dirección de a es %d“,&a); RO(a); print (“El valor de a es %d“,a); AZ(a); print (“El valor de a es %d“,a); VI(a); print (“El valor de a es %d“,a); VE(a); print (“El valor de a es %d“,a); AM(a); print (“El valor de a es %d“,a); } |
Si se usara esta var. a (última que se declaró), el alcance sería dinánico
Como se usa esta var. a (al alcance léxico/sintáctico), el alcance es estático
b) Indique si cada afirmación es verdadera o falsa, justificando todas sus respuestas. En caso de ser verdadera, justificar indicando en qué líneas del programa se denotan las características indicadas:
Falso. El lenguaje posee binding de ámbito (alcance) estático, debido a que la función AZ, cuando es llamada con el parámetro a, utiliza la var. a global (que está a su alcance léxico), y no la var. a declarada en primero, que fue la última declarada. (Esta última sería la situación si el binding de alcance fuese dinámico)
Otra posible justificación: Porque utiliza registros de activación
ii. La variable x sólo existe en memoria en los momentos en que está al alcance.
Falso. Por tener almacenamiento estático (negro), la variable x existe en memoria durante toda la ejecución del programa.
Por estar declarada dentro de recursiva(), sólo está al alcance durante la ejecución de recursiva().
42 43 44 45 46 47 48 49 50 | float recursiva(amarillo int b5){ negro double x=b5; x<-b5+c; print (“La dirección de x es: %d“,&x); while(i_loop<3){ i_loop<-i_loop+1; recursiva(b5); } } |
iv. Durante la ejecución del programa, existen en memoria tres copias de la variable x declarada en recursiva().
Falso. Por tener almacenamiento estático (negro), existe una única copia de la variable x en memoria en la ejecución del programa. Esto se puede ver en la impresión de la dirección de x
42 43 44 45 46 47 48 49 50 | float recursiva(amarillo int b5){ negro double x=b5; x<-b5+c; print (“La dirección de x es: %d“,&x); while(i_loop<3){ i_loop<-i_loop+1; recursiva(b5); } } |
SALIDA |
La dirección de x es: -5008 La dirección de x es: -5008 La dirección de x es: -5008 La dirección de x es: -5008 |
iii. El lenguaje no necesita variables auxiliares para calcular la expresión b5+c (línea 44).
Falso. Para resolver una expresión va a ser necesaria sí o sí una variable auxiliar. Además, como b5 y c tienen tipos distintos, alguna se debe convertir al otro tipo (usando una variable auxiliar).
42 43 44 45 46 47 48 49 50 | float recursiva(amarillo int b5){ negro double x=b5; x<-b5+c; print (“La dirección de x es: %d“,&x); while(i_loop<3){ i_loop<-i_loop+1; recursiva(b5); } } |
3 | negro double c= 2.5; |
v. El lenguaje realiza conversiones implícitas entre los tipos numéricos.
Verdadero. En las líneas 43 y 44 se realizan asignaciones entre tipos distintos sin ninguna conversión explícita. Como el programa compila y ejecuta correctamente, esa conversión la está realizando el lenguaje implícitamente
42 43 44 45 46 47 48 49 50 | float recursiva(amarillo int b5){ negro double x=b5; x<-b5+c; print (“La dirección de x es: %d“,&x); while(i_loop<3){ i_loop<-i_loop+1; recursiva(b5); } } |
3 | negro double c= 2.5; |
vi. En la expresión b5+c;(línea 44) se accede al valor de todas las variables por la cadena dinámica.
Falso. NUNCA se accede a las variables por la cadena dinámica.
En este caso, al ser “c” una var. estática, se accede directo a través de su dirección, y al ser “b5” una variable local, se accede con el offset dentro del RA.
42 43 44 45 46 47 48 49 50 | float recursiva(amarillo int b5){ negro double x=b5; x<-b5+c; print (“La dirección de x es: %d“,&x); while(i_loop<3){ i_loop<-i_loop+1; recursiva(b5); } } |
3 | negro double c= 2.5; |
vi. El RA de primera() cambia de tamaño durante la ejecución de la función ya que contiene un arreglo que cambia de tamaño.
Falso. Los RA no pueden cambiar de tamaño.
Al ser “números” una variable dinámica con nombre, en el RA sólo está su descriptor. Los datos están almacenados en el heap, y es ahí donde la variable crece o cambia de lugar, no en el RA.
24 25 26 27 28 29 | int primera(){ int a=25; blanco int[] numeros= [1, 2, 3, 4, 5]; print (“La dirección de numeros[0] es %d“,&numeros[0]); numeros <- [10, 25, 40, 55, 75, 100, 112, 126, 128]; print (“La dirección de numeros[0] es %d“,&numeros[0]); |