Prévia do material em texto
GCC 105 – LINGUAGENS DE
PROGRAMAÇÃO I
AULA 6 – Nomes e Vinculação II
1º Semestre de 2015
Prof. Janderson Rodrigo de Oliveira
Universidade Federal de Lavras
Departamento de Ciência da Computação
Escopo
• O escopo de uma variável é a faixa de sentenças nas quais ela
é visível.
• Uma variável é visível em uma sentença se ela pode ser
referenciada nessa sentença.
• As regras de escopo de uma linguagem determinam como
uma ocorrência em particular de um nome é associada com
uma variável.
Escopo
• Uma variável é local a uma unidade ou a um bloco de
programa se ela for lá declarada.
• As variáveis não locais de uma unidade ou de um bloco de
programa são aquelas visíveis dentro da unidade ou do bloco
de programa, mas não declaradas nessa unidade ou nesse
bloco.
Escopo Estático
• O ALGOL 60 introduziu o método de vincular nomes a
variáveis não locais, chamado de escopo estático, copiado por
muitas linguagens imperativas subsequentes.
• O escopo estático é chamado assim porque o escopo de uma
variável pode ser determinado estaticamente – ou seja, antes
da execução.
• Isso permite a um compilador determinar o tipo de cada
variável.
Escopo Estático
• Existem duas categorias de escopo estático:
– Aquelas nas quais subprogramas podem ser aninhados, as quais
criam escopos estático aninhados;
– Aquelas nas quais subprogramas não podem ser aninhados.
Escopos estáticos para subprogramas também são criados, mas
os aninhados são criados apenas por definições de classes
aninhadas ou de blocos aninhados.
• Ada, JavaScript, Fortran 2003 e PHP permitem subprogramas
aninhados, mas as linguagens baseadas em C não.
Escopo Estático
• Inicialmente vamos nos concentrar nas linguagens que
permitem subprogramas aninhados e vamos realizar uma
discussão simplificada neste cenário.
• Quando o leitor de um programa encontra uma referência a
uma variável, os atributos dessa variável pode ser
determinados por meio da descoberta da sentença na qual
ela está declarada.
• Em linguagens de escopo estático com subprogramas, esse
processo pode ser visto da seguinte forma.
Escopo Estático
• Suponha que uma referência é feita a uma variável x em um
subprograma Sub1.
• Se nenhuma declaração for encontrada em Sub1, a busca
continua nas declarações do subprograma que declarou Sub1,
chamado de seu pai estático.
• Se nenhuma declaração for encontrada lá, a busca continua
para a próxima unidade maior que envolve o pai estático de
Sub1, e assim por diante.
Escopo Estático
• O processo se repete até que uma declaração para x seja
encontrada ou a maior unidade de declaração tenha sido
buscada sem sucesso.
• Nesse caso, um erro de variável não declarada é detectado.
• Exemplo: considere o seguinte procedimento em Ada,
chamado Externo, no qual estão aninhados os procedimentos
Sub1 e Sub2.
Escopo Estático
procedure Externo is
X : Integer;
procedure Sub1 is
X : Integer;
begin -- de Sub1
...
end; -- de Sub1
procedure Sub2 is
begin -- de Sub2
... X ...
end; -- de Sub2
begin -- de Externo
...
end; -- de Externo
Escopo Estático
• Em linguagens que usam escopo estático,
independentemente de ser permitido o uso de subprogramas
aninhados ou não, algumas declarações de variáveis podem
ser ocultadas de outros segmentos de código.
• Em Ada, variáveis ocultas de escopos ancestrais podem ser
acessadas com referências seletivas, que incluem o nome do
escopo ancestral.
• Exemplo: o X declarado em Externo pode ser acessado em
Sub1 pela referência Externo.X.
Blocos
• Muitas linguagens permitem que novos escopos estáticos
sejam definidos no meio do código executável.
• Esse poderoso conceito, introduzido no ALGOL 60, permite
uma seção de código ter suas próprias variáveis locais, cujo
escopo é minimizado.
• Tais variáveis são dinâmicas da pilha, de forma que seu
armazenamento é alocado quando a seção é alcançada e
liberado quando a seção é abandonada.
Blocos
• As linguagens baseadas em C permitem que quaisquer
sentenças compostas tenham declarações e, dessa forma,
definam um novo escopo.
• Tais sentenças compostas são blocos. Exemplo:
if (list[i] < list[j]) {
int temp;
temp = list[i];
list[i] = list[j];
list[j] = temp;
}
Blocos
• Os escopos criados por blocos, que podem ser aninhados em
blocos maiores, são tratados exatamente como aqueles
criados por subprogramas.
void sub() {
int contador;
...
while (...) {
int contador;
contador++;
....
}
...
}
Blocos
• Em geral, uma declaração de uma variável efetivamente
esconde quaisquer declarações de variáveis com o mesmo
nome no escopo externo maior.
• O código do último exemplo seria permitido em C e C++, mas
seria ilegal em Java e C#.
– Os projetistas de Java e C# acreditavam que o reuso de nomes
em blocos aninhados era muito propenso a erros para ser
permitido.
Ordem de Declaração
• Em C89, como em algumas outras linguagens, todas as
declarações de dados em uma função, exceto aquelas em
blocos aninhados, devem aparecer no início da função.
• Algumas linguagens – como C99, C++, Java e C# – permitem
que as declarações de variáveis apareçam em qualquer lugar
onde uma sentença poderia aparecer em uma unidade de
programa.
Escopo Global
• Algumas linguagens, incluindo C, C++, PHP e Python,
permitem uma estrutura de programa que é uma sequência
de definições de funções, nas quais as definições de variáveis
podem aparecer fora das funções.
• Definições de variáveis fora de funções em um arquivo criam
variáveis globais, potencialmente visíveis a essas funções.
• Uma variável global em C é implicitamente visível em todas as
funções subsequentes no arquivo, exceto aquelas que
incluem uma declaração de uma variável local com o mesmo
nome.
Escopo Global
• Uma variável global em C definida após uma função pode ser
tornada visível na função declarando-a como externa, como:
• Outro exemplo: regras de visibilidade para variáveis globais
em Python.
– Uma variável global pode ser referenciada em uma função;
– Uma variável global pode ter valores atribuídos a ela apenas se
tiver sido declarada como global na função.
extern int soma;
Escopo Global
• Exemplo em Python:
• Saída:
dia = “Segunda”
def teste():
print “O dia global e: ”, dia
teste()
O dia global e: Segunda
Escopo Global
• Exemplo em Python:
• Saída:
dia = “Segunda”
def teste():
print “O dia global e: ”, dia
dia = “Terça”
print “O novo dia global e: ”, dia
teste()
UnboundLocalError
Escopo Global
• Exemplo em Python:
• Saída:
dia = “Segunda”
def teste():
global dia
print “O dia global e: ”, dia
dia = “Terça”
print “O novo dia global e: ”, dia
teste()
O dia global e: Segunda
O novo dia global e: Terça
Avaliação do Escopo Estático
• Fornece um método de acesso a não locais que funciona bem
em muitas situações.
• Desvantagens:
– Fornece mais acesso do que o necessário;
– Sistemas se software são altamente dinâmicos e os
desenvolvedores têm a tendência a descartar restrições de
acesso às variáveis e subprogramas ao realizar manutenções.
Escopo Dinâmico
• O escopo de variáveis em APL, SNOBOL4 e nas primeiras
versões de LISP era dinâmico.
• O escopo dinâmico é baseado na sequência de chamadas de
subprogramas, não em seu relacionamento espacial uns com
os outros.
• Logo, o escopo pode ser determinado apenas em tempo de
execução.
Escopo Dinâmico
procedure Externo is
X : Integer;
procedure Sub1 is
X : Integer;
begin -- de Sub1
...
end; -- de Sub1
procedure Sub2 is
begin -- de Sub2
... X ...
end; -- de Sub2
begin -- de Externo
...
end; -- de Externo
Escopo Dinâmico
• Assuma que asregras de escopo dinâmico se aplicam a
referências não locais. O significado do identificador X
referenciado em Sub2 é dinâmico.
• Ele pode referenciar a qualquer uma das declarações de X,
dependendo da sequência de chamadas.
• Uma maneira pela qual o significado correto de X pode ser
determinado em tempo de execução é iniciar a busca com as
variáveis locais.
Escopo Dinâmico
• Quando a busca por declarações locais falha, as declarações
do pai dinâmico, o procedimento que o chamou, são
procuradas.
• Se uma declaração para X não é encontrada lá, a busca
continua no pai dinâmico desse procedimento chamador, e
assim por diante, até que uma declaração de X seja
encontrada.
• Se nenhuma for encontrada, ocorre um erro em tempo de
compilação.
Avaliação do Escopo Dinâmico
• Uma referência para uma variável não local pode se referir a
diferentes variáveis não locais durante diferentes execuções
do subprograma.
• Impossibilidade de verificar os tipos das referências a não
locais estaticamente.
• O escopo dinâmico faz os programas serem muito mais
difíceis de serem lidos. Essa tarefa é praticamente impossível
para um leitor humano.
Escopo Estático e Escopo Dinâmico
• Os programas em linguagens com escopo estático são mais
fáceis de ler, mais confiáveis e executam mais rapidamente do
que programas equivalentes em linguagens com escopo
dinâmico.
Escopo e Tempo de Vida
• Algumas vezes, o escopo e o tempo de vida de uma variável
parecem ser relacionadas.
• Exemplo: considere uma variável declarada em um método
Java que não contém nenhuma chamada a método.
– Escopo: declaração – final do método;
– Tempo de vida: entrada no método – fim da execução do
método.
Escopo e Tempo de Vida
• Apesar do escopo e do tempo de vida da variável serem
diferentes, devido ao escopo estático ser um conceito textual,
ou espacial, enquanto o tempo de vida é um conceito
temporal, eles parecem ser relacionados nesse caso.
• Esse relacionamento aparente entre escopo e tempo de vida
não se mantém em outras situações.
• Em C e C++, por exemplo, uma variável declarada em uma
função usando o especificador static é estaticamente
vinculada ao escopo dessa função e ao armazenamento.
Escopo e Tempo de Vida
• Logo, seu escopo é estático e local à função, mas seu tempo
de vida se estende pela execução completa do programa do
qual ela é parte.
• O escopo e o tempo de vida também não são relacionados
quando chamadas a subprogramas estão envolvidas.
Escopo e Tempo de Vida
void imprimir() {
...
} /*Fim de imprimir*/
void calcular() {
int soma;
...
imprimir();
} /*Fim de calcular*/
Ambientes de Referenciamento
• O ambiente de referenciamento de uma sentença é a coleção
de todas as variáveis visíveis na sentença.
• O ambiente de referenciamento de uma sentença em uma
linguagem de escopo estático é composto pelas variáveis
declaradas em seu escopo local mais a coleção de todas as
variáveis de seus escopos ancestrais visíveis.
• É necessário enquanto a sentença é compilada.
Ambientes de Referenciamento
• Considere o seguinte esqueleto de programa em Ada:
procedure Exemplo is
A, B : Integer;
procedure Sub1 is
X, Y : Integer;
begin -- de Sub1
... � 1
end; -- de Sub1
procedure Sub2 is
X : Integer;
...
procedure Sub3 is
X : Integer;
begin -- de Sub3
... � 2
end; -- de Sub3
begin -- de Sub2
... � 3
end -- de Sub2
begin -- de Exemplo
... � 4
end -- de Exemplo
Ambientes de Referenciamento
• Os ambientes de referenciamento dos pontos de programa
indicados são:
Ponto Ambiente de referenciamento
1 X e Y de Sub1, A e B de Exemplo
2 X de Sub3, (X de Sub2 está oculto), A e B de Exemplo
3 X de Sub2, A e B de Exemplo
Ambientes de Referenciamento
• O ambiente de referenciamento de uma sentença em uma
linguagem de escopo dinâmico é composto pelas variáveis
declaradas localmente, mais as variáveis de todos os
subprogramas ativos.
• Um subprograma está ativo se sua execução já tiver
começado, mas não terminado ainda.
• Considere o exemplo a seguir. Assuma que as únicas
chamadas a funções são: main chama sub2, que chama sub1.
Ambientes de Referenciamento
• Considere o seguinte esqueleto de programa em Ada:
void sub1() {
int a, b;
... � 1
} /*Fim de sub1*/
void sub2() {
int b, c;
... � 2
sub1();
} /*Fim de sub2*/
voidmain() {
int c, d;
... � 3
sub2();
} /*Fim da main*/
Ambientes de Referenciamento
• Os ambientes de referenciamento dos pontos de programa
indicados são:
Ponto Ambiente de referenciamento
1 a e b de sub1, c de sub2, d da main, (c da main e b de sub2
estão ocultas)
2 b e c de sub2, d da main, (c da main está oculta)
3 c e d da main
Constantes Nomeadas
• Uma constante nomeada é uma variável vinculada a um valor
apenas uma vez.
• Constantes nomeadas são úteis para auxiliar a legibilidade e a
confiabilidade dos programas.
• A legibilidade por ser melhorada, por exemplo, ao ser usado
no nome pi em vez da constante 3.14159.
• Outro uso importante de constantes nomeadas é na
parametrização de um programa.
Constantes Nomeadas
• Exemplo em Java:
void Exemplo() {
int [] lista = new int[100];
String[] listaStr = new String[100];
...
for (indice = 0; indice < 100; indice++){
...
}
for (indice = 0; indice < 100; indice++){
...
}
...
media = soma/100;
...
}
Constantes Nomeadas
• Exemplo em Java - parametrização:
void Exemplo() {
final int tamanho = 100;
int [] lista = new int[tamanho];
String[] listaStr = new String[tamanho];
...
for (indice = 0; indice < tamanho; indice++){
...
}
for (indice = 0; indice < tamanho; indice++){
...
}...
media = soma/tamanho;
...
}