Nomes, Vinculações, Verificação de Tipos e Escopos
George Darmiton da Cunha Cavalcanti
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 cujostipos 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 desalocadaspara 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
• C++ e Ada permitem acesso a essas variáveis escondidas– Em Ada: unit.name
– Em C++: class_name::name
program main;
var x: integer;
procedure sub1;
var x: integer;
begin { sub1 }
...x...
end; { sub1 }
begin { main }
...
end; { main }
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
• Exemplo em Ada
...
declare TEMP: integer;
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 versusespacial)
• 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
BIG chama BIG chama BIG chama BIG chama SUB2SUB2SUB2SUB2
SUB2SUB2SUB2SUB2 chama chama chama chama SUB1SUB1SUB1SUB1
SUB1SUB1SUB1SUB1 usa xusa xusa xusa x
procedure big;
var x: integer;
procedure sub1;
begin { sub1 }
...x...
end; { sub1 }
procedure sub2;
var x: integer;
begin { sub2 }
...
end;
begin { big }
...
end; { big }
Nesse caso, SUB1 usa o xdeclarado em SUB2
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