Documente Academic
Documente Profesional
Documente Cultură
Aviso: Essas notas de aula tem por finalidade orientar o professor no encaminhamento das aulas, a fim
de não deixar assuntos fora da ordem planejada. A leitura deste material não reduz a necessidade de
leitura da bibliografia recomendada. Última atualização em 23/02/11.
2.1.1 Inteiros
Diferentes capacidades. Com ou sem sinal. Complemento de dois X Complemento de um.
Complemento de dois requer circuito simples. Complemento de um permite duas representações
para o zero.
2.1.3 Decimais
Ponto fixo, útil em aplicações comerciais. Algumas máquinas tinham hardware próprio para esse
tipo e representavam números como strings de bits, desperdiçando um pouco de espaço. Hoje em
dia, costuma-se usar emulação desse tipo no software.
2.1.4 Booleanos
Um único bit não é normalmente endereçável na memória.
Tipos booleanos aumentam a legibilidade da linguagem (ver exemplo na seção de expressões
booleanas).
2.1.5 Caracteres
Tamanhos fixos e variáveis.
2.1.6 Ponteiros
Proporcionam flexibilidade para outros tipos.
2.1.7 Strings
Quando são tipos primitivos, strings costumam vir acompanhas de diversos operadores para
2.1.8 Enumerados
Amentam a legibilidade do programa. Facilitam a detecção de erros de faixa/limite.
Usa constantes simbólicas para os valores possíveis. Temos problemas se uma mesma constante
aparece em mais de um tipo.
2.1.9 Subfaixas
Aumentam a legibilidade ao tornar explícitos limites de um tipo numérico. Erros podem ser
detectados em tempo de compilação. Herdam as operações do tipo “pai”.
2.1.10 Vetores
Agregado homogêneo de valores. Os valores ficam em posições consecutivas na memória.
Algumas linguagens usam parênteses para os índices, outras usam colchetes. Uso de parênteses
deixa a sintaxe semelhante à uma chamada de função.
Algumas linguagens permitem o uso de enumerados ou subfaixas como índices.
Algumas linguagens determinam o valor mínimo do índice.
Algumas linguagens não verificam os limites dos índices.
Alocação estática ou dinâmica.
2.1.12 Registros
Agregado heterogêneo. Elementos referenciados por nomes. A declaração de classes supre a
necessidade dos registros.
2.1.14 Listas
É uma estrutura de dados homogênea e recursiva. Requer que a estrutura seja percorrida para
acessar um valor. Por ser segmentada, aumenta e diminui com facilidade.
2.1.15 Conjuntos
Linguagens mais antigas podem ter limitações severas com relação à quantidade de elementos do
conjunto.
Fornecem apenas facilidade na implementação (ex.: verificar se uma letra é uma vogal).
2.2.1 Nomes
Além das variáveis, subprogramas, parâmetros, instruções (pontos no sequência de instruções de
um programa), comandos e outros elementos podem receber nomes. Nem toda variável precisa ter
nome.
Algumas linguagens limitam o tamanho permitido para os nomes. Compiladores também costumam
impor limites.
Algumas linguagens fazem distinção entre maiúsculas e minúsculas, o que é geralmente
considerado uma desvantagem.
Palavras reservadas: o significado é o mesmo em qualquer contexto.
Palavras-chave: o significado é especial em algum contexto.
Muitas vezes, palavras especiais são apenas “pré-definidas”, podendo ser redefinidas pelo
programador.
O mesmo nome pode estar associado a diferentes endereços (ex.: variáveis locais com mesmo nome
em dois subprogramas).
Nomes diferentes podem estar associados ao mesmo endereço (aliases). Aliases podem ser criados
de forma implícita (ex.: registros variantes) ou aparecer como efeito colateral do uso de ponteiros.
2.2.2 Vinculação
Uma vinculação é estática se for se a mesma durante todo o tempo de execução. Vinculação
dinâmica é aquela que pode ser alterada durante a execução.
Vinculações podem acontecer no tempo de: projeto da linguagem, implementação da linguagem,
compilação, ligação, carregamento ou execução.
2.2.6 Escopo
O escopo de uma variável é o trecho de código no qual ela pode ser referenciada.
Uma variável é local à um bloco de código se foi declarada nele. Se não foi, mas é visível, é dita
externa.
É desejável restringir tanto quanto possível o escopo das variáveis e procedimentos, para que não
haja múltiplos nomes disponíveis à um bloco de código sem necessidade.
2.2.7 Inicialização
Inicialização é a vinculação ao valor no momento da vinculação ao armazenamento. Variáveis
estáticas em termos de armazenamento precisam ser vinculadas a um valor antes da execução.
Nesse caso, pode não ser possível usar uma expressão para a inicialização.
Nem toda linguagem oferece recursos de inicialização (ex.: Pascal).
Um operador pode ter associatividade à direita (ex.: x = z = 1;) ou à esquerda (à esquerda é mais
comum). Um operador pode ser não associativo, exigindo a presença de parênteses. Quando os
operadores são de mesma precedência, a associatividade à esquerda diz que o operador da esquerda
Por serem aproximações das expressões matemáticas, os limites dos tipos podem tornar uma
expressão não-associativa (ex.: soma com números muito grades e muito pequenos).
As linguagens C, C++ e Java oferecem um operador condicional ternário (?:).
Algumas ordem podem ser mais rápidas que outras, pode ser interessante deixar que o compilador
escolha a ordem.
parte do compilador.
Não existe um consenso a respeito do que é melhor: restringir a flexibilidade das expressões ou
deixar a verificação de tipos sob a responsabilidade do programador.
As coerções de subfaixas de inteiros para inteiros, ou de inteiros pequenos para inteiros são muito
comuns.
Coerções mais profundas como a conversão de strings em valores numéricos são mais comuns em
linguagens com vinculação dinâmica de tipos.
Quase sempre, as instruções compostas são demarcas por algum elemento sintático (ex.: begin/end),
porém algumas linguagens modernas usam a endentação para demarcação.
discreto.
Nem sempre a seleção múltipla por valores discretos é adequada. Várias linguagens oferecem a
possibilidade de aninhar testes nos seletores bidirecionais (elseif/elsif/elif), tornando-os seletores
múltiplos que não sofrem da falta de legibilidade do aninhamento profundo de seletores
bidirecionais. Esse tipo de estrutura é mais genérica que a anterior (case/switch).
A instrução executada é escolhida de forma não determinística dentre as demarcas por expressões
verdadeiras.
É uma construção interessante para programação concorrente.
2.5 Subprogramas
São essenciais para a abstração de um programa, permitindo que processos completos possam ser
abstraídos numa entidade única e reusados em vários contextos. Ajudam a ressaltar a estrutura geral
de um programa.
É desejável permitir a compilação de subprogramas para criação de bibliotecas (também chamadas
de “pacotes”).
2.5.1.3 Parâmetros
São uma forma de fornecer os dados para o processamento de um subprograma. Deixam o
subprograma mais legível que o uso de variáveis externas. O escopo reduzido ajuda a evitar erros.
Parâmetros podem ser dados ou subprogramas.
A vinculação entre parâmetros reais e formais por ser feita pela posição ou pelo nome. Em algumas
linguagens pode-se usar a duas formas ao mesmo tempo (ex.: Ada e Python).
Nem sempre é obrigatório usar todos os parâmetros. Nesse caso é preciso existir alguma regra a
Por exemplo, se o parâmetro real for uma expressão constante, pode-se usar uma implementação
como a da passagem por valor (semântica de entrada). Se for uma variável, pode-se usar uma
implementação como a passagem por referência (semântica de entrada/saída).
parâmetros a um tipo de dados numa linguagem é uma forma poderosa de polimorfismo, conhecida
como “polimorfismo paramétrico”. A viabilização disso numa linguagem com tipificação forte
passa pela especificação de tipos genéricos.
Subprogramas genéricos são aqueles que apresentam um ou mais parâmetros de tipo genérico.
Exemplos de linguagens que implementam tipos genéricos: Ada, C++, Haskell e Java (a partir da
versão 1.5).
Ada foi a primeira linguagem com uma implementação para subprogramas genéricos. Nela (e em
C++, etc.) A implementação de um subprograma genérico não é transformada em código pelo
compilador e não tem efeito sobre o programa. Ela serve como um modelo (gabarito) para o
compilador que usa esse modelo para gerar código para todos os tipos que são usados na chamada
do subprograma.
De maneira mais imprecisa, pode-se dizer que o compilador gera código para vários subprogramas
com o mesmo nome, um para cada tipo usado nas chamadas, tirando do programador o trabalho de
sobrecarregar o subprograma. Em Ada, é preciso instanciar os subprogramas de forma explícita. Em
Java, o subprograma genérico gera byte-code como código comum1.
2.5.9 Co-rotinas
São um tipo bem diferente de subprograma. Elas se prestam à uma relação de igual para igual entre
unidade chamadora e chamada. Ainda assim, co-rotinas são hierarquicamente ligadas à uma unidade
de programa chamada “unidade mestra”.
A unidade mestra cria uma série de co-rotinas que “se conhecem” e as inicializa. Em seguida, o
controle é passado para uma delas que passam o controle adiante para outra co-rotina do grupo e
assim sucessivamente até eventualmente todas acabam e o controle retorna à unidade mestra.
As co-rotinas não tem um único ponto de entrada e mantém uma memória de suas ativações
anteriores. Os pontos em que passam o controle adiante são determinados pelo programador e cada
vez que uma co-rotina ganha o controle de fluxo, ela retoma a execução de onde parou. A invocação
de uma co-rotina é chamada de “retomada”.
As co-rotinas apareceram na linguagem SIMULA que tinha o propósito de oferecer facilidade para
simulação de sistemas. Um dos elementos desejados na linguagem era uma forma de proporcionar a
execução em tempo compartilhado entre unidades de programa, como se elas estivessem
executando ao mesmo tempo.
São geralmente implementadas na forma de laços que passam adiante o controle de fluxo a cada
repetição.
As co-rotinas formaram a base para a programação concorrente e são oferecidas em linguagens de
programação que enfocam esse tipo de programação. Elas também são a base conceitual do
conceito de multitarefa cooperativa.
São tidas como mais genéricas que os subprogramas comuns.
Esconder os detalhes de um tipo de dados é importante para manter a independência entre módulos
de um programa. Por exemplo: suponha que um compilador de uma determinada linguagem usa
mais bits para a mantissa de um número de ponto flutuante do que outro compilador. Nesse caso, os
programas desenvolvidos funcionam nos dois casos, e a mudança na representação de um deles não
necessita mudança no código que faz uso dele.
juntas proporcionam um suporte mais direto aos TADs. Outras facilidades oferecidas são os
construtores e destrutores que podem ser usados para gerenciar a inicialização, alocação e
desalocação de recursos (ex.: memória no heap).
O suporte de Java é muito parecido como o de C++, com algumas diferenças importantes. Todos os
tipos definidos pelo programador em Java devem ser classes. Todas as variáveis que são TADs são
alocadas no heap e acessadas por meio de referências. Além das classes, Java permite a definição de
pacotes (encapsulamento geral) e oferece o “escopo de pacote”, em que partes protegidas das
classes são acessíveis em outras partes do pacote.
tipificação dinâmica, que permite a criação de uma função que retorna algum tipo que representa
erro ou um tipo que representa o resultado do processamento.
Nas bibliotecas padrão da linguagem C, o valor de retorno de um subprograma é geralmente usado
como código de erro.
das suas instâncias. Por exemplo, suponha o tipo pessoa, como um registro que possui, entre
outras coisas, o campo gênero. É possível então declarar o tipo homem que um caso especial de
pessoa, onde o gênero é masculino.
O termo herança é usando dentro do conceito da orientação, em que, além de vermos tipos como
casos especiais de outros tipos e termos operações comuns a vários tipos, existe também a
preocupação a respeito de como essas operações alteram os valores de um tipo. A ideia básica,
entretanto é a mesma.
3. Eventos
X
Referências
[Budd 91] Budd, Timothy. An Introduction to Object-Oriented Programming. Addison-Wesly.
1991.