Nomes, Vinculações,
Verificação de Tipos e Escopos
George Darmiton da Cunha Cavalcanti
([email protected])
Tópicos
•
•
•
•
•
•
•
•
•
•
Introdução
Nomes
Variáveis
O conceito de vinculação (binding)
Verificação de tipos
Tipificação forte
Compatibilidade de tipos
Escopo e tempo de vida
Ambientes de referenciamento
Inicialização de variáveis
Introdução
• Linguagens Imperativas são abstrações da
arquitetura do computador de von Neumann
– Memória
– Processador
• Variáveis são caracterizadas por atributos
– Tipo
– Projetos de tipo
• escopo, tempo de vida, checagem de tipo, inicialização e
compatibilidade de tipos
Nomes
• Questões de projeto para nomes:
– Tamanho máximo?
– Caracteres de conexão são permitidos?
– Os nomes fazem distinção entre maiúsculas
e minúsculas?
– As palavras especiais são palavras
reservadas ou palavras-chave?
Nomes
• Tamanho
– Se muito pequeno, não são conotativos
– Exemplos de linguagens:
•
•
•
•
•
FORTRAN I: máximo de 6
COBOL: máximo de 30
FORTRAN 90 e ANSI C: máximo de 31
Ada e Java: sem limite
C++: sem limite, mas implementadores
geralmente impõem limites
Nomes
• Caracteres de conexão
– Pascal, Modula-2 e FORTRAN 77 não
permitem
– Outras linguagens permitem
Nomes
• Distinção entre maiúsculas e minúsculas
– Desvantagem:
• Legibilidade (nomes semelhantes são
diferentes)
• Pior em C++ e em Java, pois nomes possuem
maiúsculas e minúsculas
– exemplo: IndexOutOfBoundsException
– Nomes em C, em C++ e em Java fazem
distinção entre maiúsculo e minúsculo
• Os nomes em outras linguagens não fazem
Nomes
• Palavras especiais
– Usadas para tornar os programas mais legíveis ao
dar nome a ações que devem ser executadas
– Usadas para delimitar ou separar as entidades
sintáticas
• Uma palavra-chave é uma palavra que é especial em
certos contextos, por exemplo em Fortran
– Real VarName (Real é um tipo de dado seguido por um nome,
assim Real é uma palavra-chave)
– Real = 3.4 (Real é uma variável)
– Uma palavra reservada é uma palavra especial que
não pode ser usada como nome
Variáveis
• Uma variável é uma abstração de um célula
de memória
• Variáveis podem ser caracterizadas por uma
sêxtupla de atributos:
–
–
–
–
–
–
Nome
Endereço
Valor
Tipo
Tempo de vida
Escopo
Atributos das variáveis
• Nome – nem todas as variáveis possuem nome
• Endereço – o endereço de memória com o qual ela é
associada
– Uma variável pode ter diferentes endereços em diferentes
tempos no programa
– Uma variável pode ter diferentes endereços em diferentes
lugares no programa
– Se dois nomes são usados para acessar a mesma posição de
memória, eles são chamados de apelidos (aliases)
– Apelidos (aliases) são criados através de ponteiros, de
variáveis de referência e de uniões em C e em C++
– Apelidos são um problema para a legibilidade
Atributos das variáveis
• Tipo
– determina a faixa de valores das variáveis e o conjunto de
operações definidas para os valores do tipo
– no caso de ponto flutuante, o tipo também determina a
precisão
• Valor
– o conteúdo da célula de memória associada à variável
• Abstração da célula de memória
– A célula física ou o conjunto de células associadas a variável
O Conceito de Vinculação
• O valor-l de uma variável é seu endereço
• O valor-r de uma variável é seu valor
• Uma vinculação (binding) é uma associação,
como, por exemplo, um atributo e uma
entidade ou entre uma operação e um símbolo
• O momento em que uma vinculação se
desenvolve é chamado de tempo de
vinculação
Possíveis tempos de vinculações
• Tempo de projeto da linguagem
– Vinculação de símbolos de operação a operação, ex. *
• Tempo de implementação da linguagem
– Vinculação de um tipo (float) a um conjunto de valores
possíveis
• Tempo de compilação
– Vinculação de uma variável a um tipo de dado em particular
• Tempo de carregamento
– Vinculação de uma variável à célula de memória
• Tempo de execução
– Vinculação de uma variável local não-estática a uma célula
de memória
Vinculação Estática e Dinâmica
• Uma vinculação é estática se ocorrer antes
do tempo de execução e permanecer
inalterada ao longo da execução de um
programa.
• Uma vinculação é dinâmica se ocorrer
durante a execução ou puder ser modificada
no decorrer da execução de um programa
Vinculação de tipos
• Como o tipo é especificado?
• Quando a vinculação acontece?
• Tipos podem ser especificados
estaticamente por meio de alguma forma
de declaração explícita ou implícita
Declaração de variáveis:
explícita e implícita
• Uma declaração explícita é uma instrução
em um programa que lista nomes de variáveis
e especifica que elas são de um tipo particular
• Uma declaração implícita é um meio de
associar variáveis a tipos por convenções em
vez de instruções
• FORTRAN, PL/I, BASIC e Perl dispõem de
declarações implícitas
– Vantagem: capacidade de escrita
– Desvantagem: legibilidade
Vinculação dinâmica de tipos
• Vinculação dinâmica de tipos (JavaScript e PHP)
• A variável é vinculada ao tipo quando lhe é atribuído
algum valor, por exemplo JavaScript
– list = [2, 4.33, 6, 8];
– list = 17.3;
• Vantagem:
– Flexibilidade
• Desvantagens:
– Alto custo
– Difícil detecção de erros
Inferência de tipos
• Inferência de tipos (ML, Miranda e Haskell)
– Ao invés de uma instrução de atribuição, tipos são
determinados pelo contexto
• Em ML,
– fun circumf(r) = 3.14159 * r * r;
– fun vezes10(x) = 10*x;
– ML rejeita a função
– fun quadrado(x) = x * x;
–
–
–
–
Opções
fun quadrado(x:int) = x * x;
fun quadrado(x) = (x:int) * x;
fun quadrado(x) = x * (x:int);
Vinculação de Armazenamento e
Tempo de Vida
• Alocação
– Marcar\tomar uma célula de memória de um
conjunto de memória disponível
• Desalocação
– Devolver a célula ao conjunto de memória
disponível
• O tempo de vida de uma variável se inicia
quando ela é vinculada a uma célula
específica e encerra-se quando ela é
desvinculada
Categorias de variáveis baseado no
tempo de vida
•
•
•
•
Variáveis Estáticas
Variáveis Dinâmicas na Pilha
Variáveis Dinâmicas no Monte Explícitas
Variáveis Dinâmicas no Monte Implícitas
Variáveis Estáticas
• Vinculadas a células de memória antes que a
execução do programa se inicie. Exemplo:
– todas as variáveis do FORTRAN 77 e as variáveis
static do C
• Vantagens:
– eficiência (endereçamento direto)
– suporta subprogramas sensíveis à história
• Desvantagem:
– pouca flexibilidade (não permitem recursão)
Variáveis Dinâmicas na Pilha
• São aquelas cujas vinculações de armazenamento criam-se a
partir da elaboração de suas instruções de declaração, mas cujos
tipos são estaticamente vinculados (Elaboração: processo de
alocação e de vinculação de armazenamento)
• Ocorre em tempo de execução
• Exemplo
– Variáveis locais em subprogramas C e métodos em Java
• Vantagem:
– permite recursão;
– compartilhamento de espaço de memória
• Desvantagens:
– Sobretaxa de alocação e de desalocação em tempo de execução
– Subprogramas não podem ser sensíveis à história
Variáveis Dinâmicas no Monte
Explícitas
• As variáveis dinâmicas no monte explícitas são
células de memória sem nome (abstratas) alocadas e
desalocadas por instruções explícitas em tempo de
execução, especificadas pelo programador.
• Essas variáveis alocadas no monte e desalocadas
para o monte só podem ser referenciadas por meio de
variáveis de ponteiro ou de referência.
• O monte é um conjunto de células de
armazenamento altamente desorganizado, devido à
imprevisibilidade de seu uso.
Variáveis Dinâmicas no Monte
Explícitas
• Exemplo:
– Objetos dinâmicos em C++ (via new e delete)
– Todos os objetos em Java
• Vantagem:
– Convenientes para estruturas dinâmicas: listas encadeadas e
árvores
• Desvantagens:
– Dificuldade de usar ponteiros e referência corretamente
– Custo das referências para as alocações e para as desalocações
Variáveis Dinâmicas no Monte
Implícitas
• Alocação e desalocação causadas por instruções de
atribuição
– Todas as variáveis em APL
– Todas as strings e vetores em Perl e em JavaScript
• Vantagem
– Flexibilidade
• Desvantagens
– Ineficiente, pois todos os atributos são dinâmicos
– Perda de grande parte da capacidade de detectar erros
Verificação de Tipos
• Verificação de tipos é a atividade de assegurar
que os operandos de um operador sejam de tipos
compatíveis.
• Um tipo compatível é aquele válido para o
operador ou com permissão, nas regras da
linguagem, para ser convertido pelo compilador
para um tipo válido
– Essa conversão automática é chamada de coerção
• Um erro de tipo é a aplicação de um operador a
um operando de tipo impróprio
Tipificação Forte
• Uma linguagem de programação é fortemente
tipificada se erros de tipos são sempre detectados
• Vantagens
– Permite a detecção de todos os usos equivocados de variáveis
que resultem em erros de tipo
• Exemplos de linguagens
– Pascal não é: variant records (registros variante)
– C e C++ não são: permitem funções cujos parâmetros não são
verificados quanto ao tipo
– Ada é quase fortemente tipificada (Java é similar)
Tipificação Forte
• Regras de Coerção
– Podem enfraquecer a tipificação forte
– (C++ versus Ada)
– Ada possui poucas regras de coerção
• Embora Java possua metade das regras de
coerção do C++, logo sua detecção de erros é
melhor do que a do C++, sua tipificação forte
é bastante inferior a da Ada
Compatibilidade de Tipos
• Existem dois métodos diferentes de
compatibilidade de tipos
– Compatibilidade de Nome
– Compatibilidade de Estrutura
Compatibilidade de Tipo de Nome
• Significa que duas variáveis possuem tipos
compatíveis se elas estiverem na mesma declaração
ou em declaração que usam o mesmo nome de tipo
• A compatibilidade de tipo de nome é fácil de
implementar mais muito restritiva
– Uma variável subfaixa dos números dos inteiros não seria
compatível com uma variável do tipo inteiro
type indextype = 1..100; {um tipo subfaixa}
var
cont: integer;
indice: indextype
As variáveis cont e indice não são compatíveis.
cont não seria atribuída a indice e vice-versa.
Compatibilidade de Tipo de
Estrutura
• Significa que duas variáveis têm tipos
compatíveis se os seus tipos tiverem
estruturas idênticas
• Mais flexível, porém mais difícil de
implementar
Compatibilidade de Tipos
• Considere os problemas de tipo entre duas
estruturas:
– Dois registros são compatíveis no tipo se eles
possuem a mesma estrutura mas usam diferentes
nomes para os campos?
– Dois vetores são compatíveis se eles são os
mesmos exceto pela faixa de indexação?
• Exemplo: [1..10] e [0..9]
– Usando compatibilidade de tipo de estrutura não é
possível diferenciar entre tipos que tenham a
mesma estrutura
• Exemplo:
– Diferentes unidade de velocidade, ambas ponto flutuante
– Celsius e Fahrenheit, ambos ponto flutuante
Escopo
• O escopo de uma variável é a faixa de
instruções na qual a variável é visível
– Uma variável é visível em uma instrução se
puder ser referenciada nessa instrução
• As variáveis não-locais de uma unidade
ou de um bloco de programa são as
visíveis dentro deste, mas não são
declaradas lá
Escopo Estático
• Método para vincular nomes a variáveis não-locais
• Para conectar uma referência a uma variável, o
compilador precisa encontrar a declaração
• Processo de busca:
– Caso a declaração não for encontrada localmente, passa-se a
buscar em escopos mais amplos
• O pai-estático (static parent) é o subprograma no
qual encontra-se a declaração
• Os ancestrais estáticos são todos os subprogramas
até se chegar a declaração
Escopo Estático
procedure big;
var x: integer;
procedure sub1;
begin { sub1 }
...x...
end; { sub1 }
procedure sub2;
var x: integer;
begin { sub2 }
...
end;
begin { big }
...
end; { big }
A variável x em sub1 é declarada
no procedimento big
Escopo Estático
• Variáveis podem ser escondidas de uma unidade
quando a mesma possui uma variável com o mesmo
nome
program main;
var x: integer;
procedure sub1;
var x: integer;
begin { sub1 }
...x...
end; { sub1 }
begin { main }
...
end; { main }
• C++ e Ada permitem acesso a essas variáveis escondidas
– Em Ada: unit.name
– Em C++: class_name::name
Blocos
• Um método para criar novos escopos estáticos no meio do
código executável – introduzido no ALGOL 60
• Permite que uma seção de código tenha suas próprias variáveis
locais cujo escopo é minimizado
• Essas variáveis são tipicamente dinâmicas na pilha
•
– Alocada quando a seção é iniciada e desalocada quando ela é
finalizada
...
declare TEMP: integer;
Exemplo em Ada
begin
TEMP := First
First := Second
Second := TEMP
end
...
Avaliação do Escopo Estático
Assuma que MAIN chama A e B
A chama C e D
B chama A e E
Avaliação do Escopo Estático
Um grafo com chamadas potenciais
a procedimento, no sistema.
Um grafo com as chamadas
desejáveis do programa exemplo.
Avaliação do Escopo Estático
• Suponha que a especificação é alterada e E
deve acessar algum variável em D
• Soluções:
– Colocar E em D (porém, E não poderá acessar o
escopo de B)
– Mover as variáveis de D, que são necessárias em
E, para MAIN (isso permite o acesso por todos os
os procedimentos
• De maneira geral: escopo estático encoraja o
uso de variáveis globais
Escopo Dinâmico
• Baseia-se na seqüência de chamada de subprogramas,
não em suas relações espaciais (temporal versus
espacial)
• Desta forma o escopo pode ser determinado apenas
em tempo de execução
• Quando a procura por declarações locais falha, as
declarações do pai-dinâmico (procedimento de
chamada) são pesquisadas, e assim sucessivamente
• Caso nenhuma declaração for encontrada em qualquer
ancestral dinâmico, haverá um erro em tempo de
execução
Escopo dinâmico: exemplo
procedure big;
var x: integer;
procedure sub1;
begin { sub1 }
...x...
end; { sub1 }
BIG chama SUB2
SUB2 chama SUB1
SUB1 usa x
procedure sub2;
var x: integer;
begin { sub2 }
...
end;
Nesse caso, SUB1 usa o x
declarado em SUB2
begin { big }
...
end; { big }
Avaliação do Escopo Dinâmico
• Vantagem
– Conveniência
• Desvantagem
– Pouca legibilidade
• Linguagens que usam escopo dinâmico
– APL, SNOBOL4 e nas primeiras versões do LISP
– Perl também permite que as variáveis sejam
declaradas com escopo dinâmico
Escopo e Tempo de Vida
Escopo e Tempo de Vida, algumas vezes, parecem estar
relacionados, mas são conceitos diferentes
void printheader(){
...
} /* fim de printheader */
void compute() {
int sum;
...
printheader();
} /* fim de compute */
O escopo da variável sum é completamente contido pela função compute
Porém, o tempo de vida de sum estende-se ao longo do tempo durante o qual
printheader é executado
Ambientes de Referenciamento
• O ambiente de referenciamento de uma instrução é
o conjunto de todos os nome visíveis na instrução
• Em uma linguagem com escopo
– O ambiente de referenciamento é formado pelas variáveis
locais mais todas as variáveis de seus escopos ancestrais
visíveis
• Um subprograma é ativo se sua execução tiver
começado, mas ainda não tiver terminado
• Em um linguagem com escopo dinâmico
– O ambiente de referenciamento é formado pelas variáveis
locais, mais as variáveis de todos os subprogramas ativos
Constantes Nomeadas
• Uma constante nomeada é uma variável
vinculada a um valor somente no momento
em que ela é vinculada a um armazenamento
– Seu valor não pode ser mudado por uma instrução
de atribuição
• Exemplo
– uso da constante pi ao invés do valor 3,14159
• Vantagem
– Legibilidade
– Confiabilidade
Inicialização de Variáveis
• Inicializações são geralmente feitas através de
instruções de declaração
– Exemplo: em Java
– int sum = 0;
• Nem Pascal, nem Modula-2 oferecem uma
maneira de inicializar variáveis, exceto
durante a execução através de instruções de
atribuição
Resumo
• Nomes
– Tamanho; caracteres de conexão; distinção entre maiúsculas
e minúsculas; palavras especiais
• Variáveis
– nome, endereço, valor, tipo, tempo de vida, escopo
• Vinculação é a associação de atributos a entidades do
programa
• Variáveis escalares são categorizadas como
–
–
–
–
static
stack dynamic
explicit heap dynamic
implicit heap dynamic
• Tipificação forte é conceito de exigir que todos os
erros de tipo sejam detectado
Download

Topico 4 - Nome, vinculacao, Tipos, Escopo