Linguagens e Ambientes de Programação (2022/2023)
Prática 08
Ligações. Escopo estático e escopo dinâmico. Modelo de execução. Exercícios de 40 a 45.
40 - Aplicação de alguns conceitos da aula teórica 13. Considere o seguinte programa em C:
#include <stdio.h>
int a; // variável a-global
int b; // variável b-global
void p(void) { a = b; }
void f(int b) { // parâmetro b-local
int a = b; // variável a-local
p(); /* aqui */
}
int main(void) {
a = 5; b = 6; f(7);
printf("%d %d\n", a, b);
return 0;
}
- a) Qual o output deste programa, escrito em C.
- b) Qual seria o output deste programa se o C utilizasse escopo dinâmico nas chamadas de funções.
41 - No programa da pergunta 40, diga qual é ambiente no ponto indicado pelo comentário: "aqui".
42 - No programa da pergunta 40, diga qual é o âmbito da variável global "b".
43 - Escreva em C uma função polimórfica para implementar um caso particular de atribuição entre arrays: uma posição dum array recebe o conteúdo de outra posição de outro array. Use a técnica de manipulação direta de memória usando apontadores void * e a função memcpy, que foi usada na aula teórica 15. Se a cópia tiver sucesso, deve ser retornado o valor true. Se a cópia tiver insucesso, por algum dos índices ser inválido, deve ser retornado o valor false. Comece por identificar os argumentos e tipos da função e depois escreva o corpo da função. Na função main exemplifica-se uma chamada da função pretendida.
#include <stdbool.h>
#include <string.h>
bool copy(...)
{
}
int main(void)
{
const int ASIZE = 10, BSIZE = 20;
int a[ASIZE], b[BSIZE];
copy(sizeof(int), a, ASIZE, 3, b, BSIZE, 6); // a[3] = b[6]
return 0 ;
}
44 - Mostre o estado da pilha à entrada da função h, durante a execução do seguinte programa GCC. Trata-se do segundo exercício do final da aula teórica 14. É parecido com o primeiro exercício do final da aula teórica 14 (resolvido na própria aula teórica), sendo a única diferença a passagem da função h como parâmetro para a função g, o que obriga a usar uma closure.
(Extra: Já agora, veja ainda o que acontece se a função h for executada até ao fim.)
#include <stdio.h>
int i = 1, j = 1, k = 1, l = 1 ;
void f(void) {
int i = 2, j = 2 ;
void h(void) { // função aninhada
int i = 3 ;
l = i + j + k ;
}
void g(void p(void)) { // outra função aninhada com parâmetro funcional
int i = 7, j = 7, k = 7, l = 7 ;
p() ;
}
g(h) ;
}
int main(void)
{
f() ;
printf("%d\n", l) ;
return 0 ;
}
AJUDA:
- No nosso modelo de execução, a máquina abstrata possui três registos: PC (program counter), SP (stack pointer) e FP (frame pointer).
- A pilha de execução contém registos de ativação (frames) e valores intermédios de operações que ainda não foram completadas.
- Quando se chama uma função, empilha-se o respetivo registo de ativação; quando uma função retorna desempilha-se o respetivo registo de ativação.
- A zona da informação de controlo num registo de ativação possui três campos:
- DL - dinamic link - guarda o valor do FP para repor, no retorno; o DL aponta para o DL do registo de ativação imediatamente abaixo (da função que chamou).
- SL - static link - aponta para o DL da função mãe; serve para aceder às variáveis não locais.
- PC - program counter - guarda o valor do PC para repor, no retorno. Nos nossos exercícios, nunca sabemos qual é o valor exato e escrevemos '?'
- Eis o formato geral dum registo de ativação:
← [SP]
Variável local n [topo]
...
Variável local 1
Variável local 0
PC
SL
DL ← [FP]
Argumento m
...
Argumento 1
Argumento 0 [base]
|
- Quando se passa uma função como parâmetro, na implementação é preciso passar uma closure = (endereço da função, static link pré-calculado).
- Para efeitos da criação do registo de ativação inicial, imaginamos que o programa está embebido numa função sem argumentos chamada start.As entidades globais do programa são consideradas locais à função start.
|
45 - Mostre o estado da pilha de execução do seguinte programa GCC, no exato momento em que a pilha atinge a sua altura máxima.
(Extra: Já agora, depois continue a execução do programa até ao final.)
#include <stdio.h>
int fact(int x) {
if( x == 0 )
return 1 ;
else
return x * fact (x - 1) ;
}
int main() {
int a = 3 ;
int f = fact(a) ;
printf("%d\n", f) ;
return 0 ;
}
46 - Resolva o exercício que aparece no final da aula teórica 17. Confirme que a ferramenta Valgrind descobre todos os erros de execução desse programa e os assinala claramente. Não use o Eclipse, porque a compilação e a execução terão de ser feitas na linha de comando (na compilação, usar as opções -g -O0).
47 - A biblioteca "crypt", referida na teórica 17, permite adicionar aos programas suporte para passwords codificadas. Nos exercícios seguintes, use o código da página: http://www.gnu.org/software/libc/manual/html_node/crypt.html
- a) Copie, compile na linha de comando (com a opção -lcrypt), e corra o primeiro programa. Ao correr o programa, introduza uma password P do seu agrado e guarde a string S escrita pelo programa. Trata-se duma string S que representa P, mas a partir da qual é virtualmente impossível descobrir qual é esse P. A string S pode até ser tornada pública sem comprometer a segurança de P.
- b) Copie o segundo programa, trocando o valor de inicialização da variável pass pela string S gerada pelo programa anterior. Agora compile (com a opção -lcrypt) e corra o segundo programa. Verificará que o programa consegue detetar quando é introduzida a password correta P. Mas a password P continua secreta e só o dono a conhece.
Vídeos antigos
Estes vídeos poderão estar um pouco desatualizados, pois foram feitos no contexto duma edição anterior do LAP. Contudo, partes dos vídeos poderão continuar a ter alguma utilidade.
Nota: Perderam-se 2 minutos do início do vídeo por causa de problemas técnicos, mas a parte realmente importante não foi prejudicada!
Nesta 1ª parte da prática 08, resolvem-se os exercícios 44 e 45, os quais vêm na sequência da aula teórica 15.
(lap prática 08 parte2)