Buscar

exercícios compiladores

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 50 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 50 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 50 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

UNIT 1
1.
Tanto compiladores quanto interpretadores são usados para transformar um programa escrito
em uma linguagem de alto nível em código de máquina compreendido por computadores.
Porém, há algumas diferenças entre eles.
Sobre essas diferenças, tem-se que:
II. Com o uso de compiladores, a execução do programa é separada da compilação. É
executado somente depois que todo o programa de saída é compilado.
IV. Com o uso de interpretadores, a execução do programa faz parte do processo de
interpretação, portanto, é realizada linha por linha.
Você acertou!
C.
II e IV.
Estão corretas apenas as afirmativas II e IV.
Com o compilador, você não pode alterar o programa sem voltar ao código-fonte. Os programas
interpretados podem ser executados em computadores que tenham o interpretador
correspondente. Com o compilador, a execução do programa é separada da compilação,
somente depois que todo o programa de saída é compilado. Já com o interpretador, a execução
do programa faz parte do processo de interpretação, portanto é realizada linha por linha. O
interpretador lê uma única instrução e mostra o erro, se houver. Você deve corrigir o erro para
interpretar a próxima linha. Já com o compilador, são exibidos todos os erros e avisos no
momento da compilação. Portanto, você não pode executar o programa sem corrigir erros.
2.
Compilador e interpretador são duas maneiras diferentes de traduzir um programa de
linguagem de programação ou script para linguagem de máquina. São linguagens de
programação compiladas: 
Você acertou!
B.
Java, C e C++.
C, C++, C# e Java são linguagens de programação que usam compiladores. Já PHP, Perl, Ruby
e Python utilizam interpretadores.
3.
O processo de compilação de um código em linguagem de alto nível em um código de
máquina ocorre de forma sequencial. A primeira etapa desse processo é a análise léxica,
na qual:
I. Os lexemas são montados.
II. É criada uma representação intermediária como a de uma árvore.
III. Os tokens são produzidos.
IV. Aponta-se para uma entrada na tabela de símbolos.
É correto afirmar apenas o que está em:
Você acertou!
D.
I, III e IV.
Estão corretas as afirmativas I, III e IV.
A análise léxica é a primeira fase da compilação, na qual o fluxo de caracteres que compõem
o programa de origem é lido e, também, ocorre o agrupamento dos caracteres em sequências
significativas — lexemas. Para cada um desses lexemas, o analisador léxico produzirá um token
como saída, que será repassado para a fase seguinte. Nele, o primeiro componente
compreende um símbolo abstrato que será utilizado na análise de sintaxe e o segundo aponta
para uma entrada na tabela de símbolos para esse token.As informações da entrada da tabela
de símbolos são necessárias para a análise semântica e a geração de código. A representação
intermediária, semelhante a uma árvore que representa a estrutura gramatical do fluxo de token,
é criada na fase de análise sintática.
4.
Compilar compreende a criação de um programa executável a partir de um código escrito em
uma linguagem de programação compilada. A compilação permite que o computador
execute e compreenda o programa sem a necessidade do software de programação
usado para criá-lo. Em uma das etapas desse processo de compilação, temos a verificação
do programa de origem, em que se observa se há consistência considerando a linguagem
definida tomando como base as informações na tabela de símbolos. Qual é essa etapa?
Você acertou!
C.
Análise semântica.
A análise léxica faz uma varredura, montando os lexemas e agrupando os tokens. Na análise
sintática, os tokens são utilizados e se cria uma representação intermediária semelhante a uma
árvore que representa a estrutura gramatical do fluxo de tokens. A partir dessa árvore, o
analisador semântico faz a verificação do programa de origem sobre se há consistência
semântica considerando a linguagem definida tomando como base as informações na tabela de
símbolos. Depois, o analisar semântico junta todas as informações de tipo e as guarda na árvore
de sintaxe ou na tabela de símbolos, para que sejam utilizadas mais tarde durante a geração de
código intermediário. Na geração de código intermediário, um compilador pode construir uma ou
mais representações intermediárias, que podem ter variadas formas, ao longo do processo de
tradução de um programa de origem em código de destino. Na etapa de otimização de código,
tenta-se melhorar o código intermediário para que o código de destino seja melhor em termos de
velocidade, ou outras coisas relacionadas ao seu tamanho (mais curto) ou consumo de energia.
5.
Na prática, as etapas do processo de compilação podem ser agrupadas, e as representações
intermediárias entre elas não precisam ser construídas de forma explícita. Um componente
muito importante nesse processo é a tabela de símbolos. Sobre essa tabela, qual das
alternativas está correta?
Você acertou!
E.
Deve ser projetada para permitir que o compilador encontre o registro para cada nome de forma
rápida.
A tabela de símbolos é uma estrutura de dados com um registro para cada nome de variável,
com campos para os atributos do nome. Essa estrutura deve ser projetada para permitir que o
compilador encontre o registro para cada nome de forma rápida e, depois, armazene ou
recupere os dados desse registro rapidamente. O registro dos nomes das variáveis utilizadas no
programa de origem e a coleta das informações sobre os diferentes atributos de cada nome são
funções extremamente importantes de um compilador. Ainda, a tabela não armazena outras
informações que não estejam relacionadas ao programa de origem. Lembrando que a tabela de
símbolos é usada por todas as fases do compilador, armazenando informações do programa de
origem.
1.
Na construção de compiladores e no uso de linguagens de programação em geral, expressões
regulares constituem um poderoso instrumento para a validação de textos. Nesse contexto,
analise a expressão regular exibida a seguir:
a{1,4}b*c+
Assinale a cadeia que faz parte dessa expressão:
Resposta correta.
E.
aabbbc.
Lembrando os conceitos de expressões regulares:
? ➜ Zero ou um;
* ➜ Zero, um ou mais;
+ ➜ UM ou mais;
{n,m} ➜ De n até m.
Assim, a primeira alternativa não contém o caractere c, o que a torna inválida. A outra alternativa
contém um a (1 a 4 caracteres a) e um c (um ou mais c). A alternativa aaccbb está de acordo
por conter dois a e dois c. As alternativas restantes contêm, respectivamente, um b e três b em
sua expressão, estando de acordo com a notação b* (0 ou mais b.)
2.
As palavras-chave são usadas para expressar estruturas da linguagem, como comandos
condicionais, de repetição, etc. Geralmente são reservadas, não podendo ser utilizadas como
identificadores.
Entre as opções a seguir, considerando a linguagem de programação Java, assinale a que
contém uma cadeia que pode ser usada como identificador.
Resposta correta.
B.
SourceT.
A palavra System é usada em algumas linguagens como uma chamada para alguma operação
(como o System.outo.print, do Java). SourceT não faz parte das palavras reservadas, podendo
ser usado como identificador normalmente. O comando whileé uma estrutura comum de
repetição; null,em algumas linguagens, é usado para representar vazio ou nulo; e int é uma
expressão tipada, ou seja, um tipo de variável em linguagens de programação.
3.
Observe o código Java da imagem a seguir para a realização da questão:
Das opções a seguir, a que representa corretamente um par (token, lexema) que pode estar
relacionado com a figura é: 
Resposta correta.
D.
(ID, soma).
Considerando os padrões para a análise léxica, while é reservado e não pode ser usado como
identificador. Na sequência, há uma ambiguidade, em que o termo soma aparece tanto como
token como lexema, sendo que soma é um identificador da linguagem. Em seguida, NUM é
incompatível com x, pois este é um identificador. ID e soma estão corretos em sua combinação,
e x já é um lexema, e não será usado como token para a numeração.
4.
Os autômatos finitos são uma importante ferramenta que facilitaa conversão de expressão
regular para um modelo melhor compreensível pela linguagem de programação, muito usado
também como um analisador léxico. Considere o seguinte AFD:
É correto afirmar sobre esse AFD:
I. Apresenta, no mínimo, um número inteiro com dois caracteres.
II. Aceita o lexema 002.002.
III. Sempre se iniciará com um número.
 IV. Se símbolo ponto (.) já foi lido, então de certo o autômato analisador estará em algum estado
final.
Está correto o que se afirma em:
Resposta correta.
B.
apenas II.
Pode-se notar que o autômato inicia uma transição com um dígito (0 a 9) ou com um ponto (.).
Se a entrada for, por exemplo, 5.4, o autômato aceita e realiza sua classificação conforme
conveniente. Portanto, o item I está incorreto. Para o item II, o primeiro '0' ativa a transição de q0
para q1. Na sequência, 0 e 2 permanecem em q1. Ao ser lido o ponto, o autômato passa para
q2 e permite qualquer quantidade de dígitos posteriormente. Portanto, o item II está correto. O
item III afirma que sempre aceitará cadeias que iniciem apenas em números, mas a cadeia .05,
por exemplo, é aceita e, portanto, torna o item III incorreto. Quando um ponto for lido antes de
qualquer número (como primeiro caractere), o autômato passará de q0 para q3. Observe que,
apesar de já se ter lido um ponto, ainda não está no estado final, pois aguarda pelo menos um
dígito. Portanto, o item IV está incorreto.
5.
Sobre os compiladores e suas análises léxicas e sintáticas, analise os itens a seguir:
I. Um dos componentes léxicos de uma linguagem se refere às palavras reservadas. No caso da
linguagem Pascal, entre as palavras reservadas, estão: AND, ARRAY, BEGIN, CONST, DIV,
ELSE, FUNCTION, LABEL, MOD, NOT, OF, OR, PROCEDURE,
PROGRAM,RECORD,REPEAT,TO,TYPE, UNTIL, VAR, WHILE.
II. A análise léxica/sintática de uma linguagem que tem palavras reservadas tende a ser mais
complexa que a de linguagens que têm apenas palavras-chave usadas também como
identificadores.
III. Um dos componentes sintáticos de uma linguagem se refere aos identificadores. No caso da
linguagem Pascal, estes são cadeias de caracteres contendo letras ('A', ..., 'Z', 'a', ..., 'z'), dígitos
('0', ... '9') e o caractere sublinhado ('_'), podendo o primeiro caractere ser uma letra ou número.
IV. Se uma regra diz que um token se estende até que seja encontrado um caractere que não
faz parte dele, essa regra permitiria a um analisador léxico reconhecer em XYZ + 1 uma
ocorrência de um identificador XYZ.
V. Na linguagem Pascal, não se faz distinção entre maiúsculas e minúsculas em identificadores
e palavras reservadas. Em C, no entanto, identificadores como TabSimb e tabsimb são distintos,
permitindo que identificadores relacionados tenham formas semelhantes.
Está correto o que se afirma em:
Você acertou!
C.
I, IV e V, apenas.
O item I apresenta diretrizes corretas, pois palavras reservadas também são componentes
léxicos de acordo com os exemplos mostrados no item. Em II, afirma-se que palavras-chave
podem ser usadas como identificadores, o que é incorreto. Em III, o texto termina dizendo que
se pode iniciar por número, porém a grande maioria das linguagens não permite o início de um
identificador de forma numérica. Em IV, é descrita uma regra aceita para a leitura da cadeia em
questão. Em V, é fato que Pascal pode não diferenciar, mas C, Java e outras linguagens
diferenciam maiúsculas de minúsculas (ainda que não se recomende nomes iguais, mesmo com
diferenciação de maiúsculas, para evitar confusões em uma análise humana).
1.
A análise léxica é a primeira fase do projeto do compilador. Um lexer pega o código-fonte
modificado, que é escrito na forma de frases. Em outras palavras, ajuda a converter uma
sequência de caracteres em uma sequência de tokens. O analisador léxico divide essa sintaxe
em uma série de tokens. Ele remove qualquer espaço extra ou comentário escrito no
código-fonte.
Sobre conceitos relacionados à análise léxica, uma sequência de caracteres que representa
uma unidade de informação no programa de origem é chamada de:
Resposta correta.
A.
token.
O token é a informação necessária que contém a sequência e a classificação para o analisador
léxico. O lexema são as cadeias de caracteres que servirão como entrada para a classificação.
O padrão está relacionado à descrição usada pelo token. É uma sequência de caracteres. O
lexer é o analisador léxico em si e as regras se referem às expressões regulares nos critérios
que o analisador vai utilizar para a aceitação e a classificação dos lexemas.
2.
Um dos importantes componentes que fazem parte de uma análise léxica é a tabela de
símbolos, que fornece ao analisador as relações entre lexemas e identificadores para o
reconhecimento dos padrões de entrada. Considere o seguinte código, que é uma entrada para
o analisador léxico:
Considere que, na tabela de símbolos do analisador, existem, dentre outras, as classificações
identificador, palavra-chave e operador, de modo que todos os símbolos, para simplificação,
entrariam nesta última categoria.
Sendo assim, qual alternativa apresenta todas as classificações de forma correta?
Resposta correta.
C.
int - palavra_chave
 } - operador
Na classificação fornecida, x, y e maximum são considerados identificadores. Os lexemas int, if,
else e return são palavras_chave. Os símbolos restantes, pela regra mencionada, serão
considerados operadores.
3.
No gerador de um analisador léxico, cada expressão regular está associada a uma "frase" em
uma linguagem de programação que avaliará os lexemas que correspondem à expressão
regular. A ferramenta, então, constrói uma tabela de estados para a máquina de estados finitos
apropriada e cria o código do programa que contém a tabela, as frases de avaliação e uma
rotina que as usa apropriadamente.
Sobre o analisador léxico, sua geração e aplicação, analise os itens a seguir:
 I. O analisador léxico é usado por navegadores da web para formatar e exibir uma página da
web com a ajuda de dados analisados de JavaScript, HTML e CSS.
II. Um passo importante que os lexers devem realizar (de acordo com as regras na
programação) é a priorização do tokens, pois lexemas com partes semelhantes podem ser
ambíguos.
III. Os comentários e caracteres de tabulação não são identificados pelo analisador, não
precisando impor alguma regra para que ele possa reconhecê-los adequadamente.
Está correto o que se afirma em:
Resposta correta.
B.
I e II.
Item I – De fato, os navegadores utilizam o analisador léxico para formatação de URL,
verificação de código JavaScript, CSS e HTML para formatação, identificação e exibição correta
de uma página.
Item II – Podemos citar como exemplo o lexema while, sendo que, se sua regra vier depois da
dos identificadores (que aceitam iniciar com caracteres de (a-z), ele será classificado como
identificador, e não como while ou palavra reservada.
Item III – No gerador de um lexer, é preciso apresentar os caracteres especiais e ações que
devem (ou não) ser realizadas quando esses forem encontrados.
4.
O reconhecimento de cadeias de símbolos por um analisador léxico é realizado a partir de um
conjunto de regras estabelecidas desde sua geração, com base nas chamadas "expressões
regulares".
Deseja-se gerar um analisador léxico que reconheça as palavras "Internet", "internet",
"INTERNET" e exiba a frase "INTERNET CONECTADA" quando for reconhecido. O trecho e
expressão regular, no JFlex, que pode representar essa regra é:
Você acertou!
D.
 %%//REGRAS (Internet|INTERNET|internet) {System.out.println("INTERNET CONECTADA");}
Para que a regra esteja correta, quando falamos de caracteres, podemos usar o colchete [X|x],
separando por ou ( | ). Para expressões maiores, o gerador precisará ter a regra entre
parênteses, como (Internet | INTERNET | internet). Além disso, como o JFlex é construído em
Java, é necessário obedecer à sintaxe da linguagem. Logo, a exibição correta na saída do
sistema se dá pela expressão System.out.println (pode ser apenas System.out.print caso não se
queira pular uma linha nasaída). O texto deverá estar entre aspas duplas.
5.
Um analista de tecnologia da informação (TI) criou uma expressão regular para verificar quantas
vezes o nome de sua empresa é citado em determinado site. Considerando que já é feito um
tratamento na digitação e que sempre são passados ao analisador caracteres em maiúsculo, a
expressão criada pelo analista para o JFlex foi:
(EMPRESAX) {contNome++;}
A variável contNome foi definida corretamente na fase das predefinições, iniciada em zero.
Sobre esse caso, analise os itens a seguir:
I. A expressão criada está correta, atendendo à especificação da entrada em String dentro do
parêntese e realizando a contagem de acordo com as normas da linguagem.
II. Não há necessidade da inserção do comando de exibição de saída, uma vez que não é um
requisito desejado a priori.
Marque a alternativa que apresenta a análise correta dos itens.
Resposta correta.
C.
I e II estão corretos, uma vez que a expressão atende às regras do analisador; o requisito
primordial era apenas a contagem de ocorrências do termo em questão.
Os padrões estão sendo corretamente atendidos. O item I cita a expressão correta, uma vez que
os parênteses estão sendo empregados corretamente com o nome a se avaliar. Como a
intenção maior é a contagem de ocorrências, não é necessário imprimir algum resultado para
usuário, conforme citado no item II, a não ser que fosse requisito e usado para outra operação.
1.
A análise sintática é feita na fase da compilação, que analisa a estrutura da linguagem
baseada em uma gramática. Alguns algoritmos são implementados dependendo da gramática.
Leia as afirmações a seguir:
I – O algoritmo LL(1) pode ser executado em gramáticas ambíguas e com recursividade à
esquerda.
II – O algoritmo com backtracking é do tipo top-down e tem um custo de execução alto.
III – O algoritmo LR(0), tipo top-down, é executado em gramática com recursividade à esquerda.
IV – Algoritmos baseados na precedência de operadores são do tipo ascendente e operam com
ambiguidade de gramática.
Quais estão corretas?
Você acertou!
B.
II e IV.
A gramática, na análise sintática, são as regras impostas à estrutura que derivam em uma
linguagem. Uma linguagem de livre contexto foi derivada de uma gramática de livre contexto e
alguns algoritmos são implementados de forma descendente (top-down) e ascendente
(bottom-up).
Sendo assim, tem-se que:
I – A afirmação é falsa, pois o algoritmo da família LL não pode ser executado em gramáticas
ambíguas e com recursividade à esquerda.
II – A afirmação é verdadeira, pois o algoritmo com backtracking é do tipo top-down e tem um
custo de execução alto.
III – A afirmação é falsa, pois os algoritmos da família LR, tipo bottom-up, é executado em
gramática com recursividade à esquerda.
IV – A afirmação é verdadeira, pois o algoritmo que trabalha com precedência de operadores
(exemplos: *,+, -, /) e potência é do tipo ascendente e pode ser executado em gramáticas
ambíguas.
2.
As gramáticas livres de contexto são usadas para definir linguagens de programação, bem como
os seus compiladores. Uma gramática de livre contexto é definida por uma quádrupla G = { V, T,
P, S}. Os elementos básicos que formam as regras da gramática são os símbolos terminais,
formados por elementos de um alfabeto Σ, e os símbolos não terminais, comumente chamados
de variáveis.
Agora, considere as seguintes afirmações:
I – Os algoritmos da família LR são executados de forma ascendente (bottom-up), ou seja,
partem de uma cadeia para chegar a uma variável inicial.
PORQUE
II – Fazem uso de uma palavra de entrada, um parser, uma pilha de análise sintática e uma
tabela construída por meio de coleções canônicas de itens.
Assinale a alternativa que apresenta a análise correta das afirmações:
Você acertou!
B.
As afirmações I e II são proposições verdadeiras e a II justifica a I.
Os algoritmos da família LR podem ser LR(0), LR(1), LALR(1) e SLR(1), sendo executados de
forma ascendente (bottom-up), ou seja, partem de uma cadeia para chegar a uma variável inicial
e, como estrutura, fazem uso de uma palavra de entrada, um parser, uma pilha de análise
sintática e uma tabela construída por meio de coleções canônicas de itens.
3.
As regras sintáticas que representam a definição da linguagem, indicando como símbolos
terminais e não terminais podem ser combinados, são conhecidas como regras de produção.
Considere a expressão 1 + 2 * 3. Pode-se utilizar o algoritmo de precedência de operadores
para fazer a análise sintática dessa sentença.
Considerando o contexto, marque V para verdadeiro e F para falso:
( ) Pode existir uma ambiguidade na gramática, pois o algoritmo por precedência de operadores
consegue resolver a ambiguidade.
( ) Os níveis mais baixos da árvore sintática criada pelo algoritmo de precedência têm maior
prioridade na execução.
( ) O algoritmo de precedência faz uso de uma tabela de precedência, em que o símbolo
identificador * tem maior prioridade entre os operadores.
( ) O look-ahead do vetor da palavra de entrada aponta para somente um caractere por vez.
Agora, assinale a alternativa que apresenta a sequência correta:
Resposta correta.
A.
V – F – V – V.
O algoritmo bottom-up por precedência de operadores é utilizado em gramática em que há
operações aritméticas e pode ser usado em gramáticas ambíguas, pois faz uso de uma tabela
de prioridades de identificadores de operação como *, +, -, entre outros.
Sendo assim, a sequência correta é:
(V) A afirmativa é verdadeira, pois a ambiguidade na gramática é resolvida pela tabela de
precedência de prioridade utilizada pelo algoritmo.
(F) A afirmativa é falsa, uma vez que a tabela de precedência determina a prioridade dos
operadores, sendo que os de maior prioridade estão localizados no nível mais baixo da árvore
sintática.
(V) A afirmativa é verdadeira, já que uma tabela de precedência, prioridade de operações, é
utilizada pelo algoritmo no processo de parsing.
(V) A afirmativa é verdadeira, pois o ponteiro do vetor conhecido como look-ahead aponta para
um caractere por vez na palavra de entrada.
4.
Uma linguagem é baseada em uma gramática. A gramática determina as regras a serem
aplicadas em determinada linguagem. Uma gramática tem variáveis, expressões e
identificadores.
Considere a gramática a seguir:
E=> E+T | T
T=> T*F | F
F => a | b
Assinale a alternativa correta. 
Resposta correta.
C.
O algoritmo ascendente de precedência de operadores pode ser aplicado a essa gramática.
A gramática apresenta uma recursividade à esquerda e operações matemáticas. Sendo assim, o
algoritmo ascendente por precedência de operadores é o mais indicado para essa gramática,
pois resolve o problema da associatividade pela tabela de precedência.
As demais sentenças estão incorretas, pois o algoritmo LL(1) não é indicado para gramáticas
com recursividade à esquerda. Os algoritmos da família LR não usam tabela de precedência. Já
o algoritmo de precedência usa a tabela de precedência de operadores.
5.
Existe uma extensa literatura sobre a teoria da gramática e a análise sintática. Possivelmente,
uma das obras mais famosas nessa área seja An efficient context-free parsing algorithm, de
Earley J. (1970), sendo mais orientado para o compilador de gramáticas livres de contexto.
Assinale a alternativa que preenche corretamente as lacunas:
Os ___________ de compiladores tendem a ser muito conservadores em sua escolha de
técnicas de análise para linguagens de programação convencionais. Existem boas razões
práticas para isso, mas há momentos em que técnicas diferentes, e talvez mais poderosas, são
requeridas. Uma ___________ livre de contexto define recursivamente vários conjuntos de
strings. Cada conjunto é denotado por um nome, que é chamado de ___________. O conjunto
de não terminais é disjunto do conjunto de terminais. Um dos não terminais é escolhido para
denotar a linguagem descrita pela ___________. Isso é chamado de símbolo inicial da
gramática. Os sets são descritos por várias produções. Cada produção descreve algumasdas
strings possíveis que estão contidas no conjunto denotado por um não terminal. Uma
___________tem a forma do conjunto de símbolos não terminais e terminais associados entre si
e são as derivações de regras da gramática.
Assinale a alternativa que preenche as lacunas de forma correta.
Você acertou!
A.
desenvolvedores – gramática – não terminais – gramática – produção.
Os desenvolvedores de compiladores tendem a ser muito conservadores em sua escolha de
técnicas de análise para linguagens de programação convencionais. Existem boas razões
práticas para isso, mas há momentos em que técnicas diferentes, e talvez mais poderosas, são
requeridas. Uma gramática livre de contexto define recursivamente vários conjuntos de
strings.Cada conjunto é denotado por um nome, que é chamado de não terminal. O conjunto de
não terminais é disjunto do conjunto de terminais; a maioria dos não terminais é escolhida para
denotar a linguagem descrita pela gramática. Isso é chamado de símbolo inicial da gramática.
Os sets são descritos por várias produções. Cada produção descreve algumas das strings
possíveis que estão contidas no conjunto denotado por um não terminal. Uma produção tem a
forma do conjunto de símbolos não terminais e terminais associados entre si e são as
derivações de regras da gramática.
UNIT 2
1.
Um analisador sintático cria uma árvore sintática para analisar se determinada sentença faz
sentido em dada gramática ou não. Essa estrutura de dados pode ser percorrida da raiz para as
folhas ou das folhas para a raiz.
Dessa maneira, a forma como esse processo é feito determina se a análise sintática é
descendente ou ascendente. Com relação à análise sintática descendente, assinale a alternativa
correta.
Você acertou!
A.
Esta análise é realizada processando a entrada da esquerda para a direita, buscando os
símbolos terminais e os colocando nas posições corretas na árvore sintática antes de andar
para a próxima regra de produção.
Na análise sintática, a sentença com base na fórmula sentencial xAα é descrita por elementos
de significado, denominados tokens, em que os símbolos terminais devem sempre ser levados à
esquerda da produção. Nessa fórmula, A é um símbolo não terminal, α pode ser uma cadeia de
símbolos terminais ou não terminais e x é uma cadeia de símbolos terminais e que pode ser
nula, ou seja, a cadeia pode se iniciar com um símbolo não terminal.
Uma gramática deve ser completamente descrita para ser funcional. Caso contrário, o
analisador sintático pode ou não conseguir interpretar uma sentença devido à falta da produção
relevante ou pode ser que uma produção seja interpretada de forma errada no caso de
ambiguidade, quando é possível criar mais de uma árvore sintática para a mesma sentença.
Esse tipo de construção pode gerar erros de precedência de operadores, dependendo de como
a árvore sintática é criada.
Assim, essa análise é realizada processando a entrada da esquerda para a direita, buscando os
símbolos terminais e os colocando nas posições corretas na árvore sintática antes de andar
para a próxima regra de produção.
2.
Um dos algoritmos utilizados para a análise sintática de uma expressão é o LL, que percorre a
sentença da esquerda para a direita.
Com relação ao algoritmo LL, assinale a alternativa correta.
Você acertou!
C.
É utilizado para a análise sintática e seu nome vem das suas etapas: inicialmente, verificando da
esquerda para a direita e a derivação mais à esquerda.
Com relação ao algoritmo LL, é possível afirmar que ele é utilizado para a análise sintática e seu
nome vem das suas etapas: inicialmente, verificando da esquerda para a direita e a derivação
mais à esquerda.
O algoritmo LL realiza a análise da gramática elaborando a árvore sintática da expressão a ser
analisada e esse processamento é feito sempre da esquerda para a direita. Para melhorar o
processamento das expressões, alguns cuidados devem ser adotados, como a fatorização à
esquerda para eliminar a ocorrência de uma sequência de símbolos em duas ou mais regras de
produção, e a manipulação das produções para manter à esquerda sempre um símbolo
terminal, eliminando, assim, a recursão à esquerda. Ainda, deve se garantir que a gramática
utilizada não é ambígua, ou seja, que não é possível criar mais de uma árvore sintática por
sentença. O funcionamento do algoritmo, entretanto, é simples; dado um símbolo não terminal N
qualquer, com o símbolo c inicial, a regra de produção que se deve escolher é uma regra em
que c seja um dos símbolos iniciais ou em que é o símbolo seguinte a uma produção vazia.
3.
Uma das estratégias para a construção da árvore sintática de uma sentença é a análise sintática
ascendente.
Como relação a essa técnica, assinale a alternativa correta.
Você acertou!
B.
A árvore sintática da sentença é criada a partir das folhas da árvore e chegando na raiz. Caso
seja possível realizar essa operação, a sentença é válida.
Para a criação da árvore sintática utilizando os algoritmos de análise sintática ascendente, os
algoritmos percorrem a sentença da direita para a esquerda, tentando transformar os símbolos
terminais em não terminais por meio de processos de deslocamento e redução; até mesmo o
sentido de processamento diferente do padrão de leitura humana se aplica a todo tipo de
sentença. O resultado final, porém, é que a sentença pode não ser válida dentro da gramática.
Assim, a árvore sintática é criada das folhas para a raiz e a gramática utilizada é a mesma que a
usada para outras formas de processamento. Ou seja, não precisa ser colocada em um padrão
para esse processamento.
O elemento central desse processo é o autômato de pilha, também denominado de PDA, uma
vez que consegue executar as funções de movimentação e redução dos tokens da sentença
utilizando uma pilha para o seu processamento, com a aplicação das duas funções
características dessa estrutura de dados: a inserção de elementos na pilha; e a retirada de
elementos da pilha.
4.
Especificar uma linguagem, com sua gramática, léxico e sintaxe, é apenas o primeiro passo
para o desenvolvimento da linguagem. A próxima etapa é a criação de um compilador para essa
linguagem, sendo tipicamente realizada com o auxílio de ferramentas para a construção de
analisadores sintáticos.
Com relação a esses componentes, assinale a alternativa correta.
Resposta correta.
C.
As ferramentas de construção de analisadores semânticos são utilizadas para gerar formas de
interpretar e validar sentenças criadas a partir de uma gramática.
Essas ferramentas foram criadas para criar gramáticas e produções próprias, desenvolver uma
linguagem e implementá-la utilizando esses softwares.
Essa liberdade é fornecida ao usuário devido ao fato de que a ferramenta provê liberdade para o
desenvolvedor inserir as regras de produção que desejar nos
arquivos-fonte. Tanto o ANTLR, desenvolvido sobre a máquina virtual Java e tem características
e sintaxe similares às orientadas a objetos,
quanto o Yacc, desenvolvido para o Linux e que tem características de linguagem imperativa,
são distribuídos sob licenças de software livre. Portanto, podem ser utilizados para qualquer
propósito ou aplicação.
Ao se utilizar esse método para a criação de elementos capazes de processar uma gramática, é
possível implementar linguagens e aplicar esses módulos em novos softwares sem precisar
realizar do zero a implementação de um analisador sintático. Afinal, esses componentes
permitem que sejam escritas funções que serão executadas quando a sentença
for processada. O código da definição de uma linguagem tem um campo em que é possível
inserir as funções que o sistema irá executar ao se deparar com a produção em questão.
5.
A ambiguidade em gramáticas é a possibilidade de se criar mais de uma árvore sintática a partir
de uma sentença.
Com relação a gramáticas ambíguas, assinale a alternativa correta.
Você acertou!
E.
A ambiguidade é indesejada em linguagens. Quando detectada, deve ser corrigida ou pode não
ser possível dar o significado correto a um programa.
As gramáticas ambíguas estão sujeitasà criação de softwares com funcionamento imprevisível,
uma vez que pode não ser possível verificar exatamente qual é a função esperada pelo
desenvolvedor do código-fonte. A ambiguidade não é uma característica intrínseca às
linguagens livres de contexto e é indesejada, pois impede que o compilador determine quais
funções o código-fonte deve executar. Ela impede o compilador de determinar exatamente o que
um código-fonte diz, já que um mesmo token pode estar ligado a mais de uma expressão. Logo,
caso exista essa característica em uma linguagem, deverá ser eliminada por meio das
operações corretas.
Caso uma produção apresente ambiguidade, a gramática toda é considerada ambígua e deve
ser revista. Como uma produção nunca é aplicada isoladamente, a produção ambígua já é o
suficiente para determinar a gramática inteira como ambígua.
1.
A utilização de expressões regulares pode facilitar a validação de dados, a procura por palavras
em determinado texto, o desenvolvimento de analisadores sintáticos, etc. Além disso, as
expressões regulares aumentam a produtividade e reduzem o tempo de busca em função dos
padrões estabelecidos. Em relação ao conceito e às características das expressões regulares,
analise as afirmações a seguir:
I. As expressões regulares foram desenvolvidas para auxiliarem a comunicação somente de
desenvolvedores e usuários quanto aos padrões definidos.
II. Um lexema pode ser definido como um conjunto de símbolos sequenciais cujo padrão é
aceito por uma linguagem de programação.
III. Uma expressão regular, baseada em um conjunto de caracteres, consegue identificar
diferentes padrões em um texto, como um número de IP (10.49.1.1) e o e-mail de um usuário
(xxx@xxx.com.br).
IV. Uma linguagem baseada em expressões regulares não pode conter somente a palavra vazia.
Agora, assinale a alternativa que apresenta a resposta correta. 
Você acertou!
B.
Apenas as afirmativas II e III estão corretas.
Apenas as afirmativas II e III estão corretas. A afirmativa I está incorreta, pois as expressões
regulares auxiliam na comunicação entre os usuários, desenvolvedores e as máquinas. A
afirmativa II está correta, pois a sequência de caracteres reconhecidos por um padrão é
chamada de "lexema". A afirmativa III está correta, pois, dada uma string, uma expressão
regular consegue reconhecer, encontrar ou validar um padrão em um texto, como data, horário,
número de IP, nome, RG e CPF de uma pessoa, declaração de uma função(), dados que estão
entre <tags></tags>, endereço de e-mail, um nome de usuário ou senha específicos, entre
outros padrões. A afirmativa IV está incorreta, pois uma linguagem L(r), sendo r uma expressão
regular, pode ser definida a partir de uma linguagem que contém somente a palavra vazia – {Ɛ}.
2.
A linguagem de programação C# utiliza as expressões regulares e as gramáticas livres de
contexto para validar expressões e a gramática da linguagem escrita no arquivo .c.
Assinale a alternativa correta que apresenta a biblioteca de expressões regulares, a unidade de
compilação e o que a gramática gera ao ser compilada em uma linguagem C#. 
Você acertou!
A.
System.Text.RegularExpression, compilation_unit e árvore sintática.
A biblioteca de expressões regulares consiste em (System.Text.RegularExpression), a unidade
de compilação é (compilation_unit), e o que a gramática gera ao ser compilada é a árvore
sintática da linguagem C#. System. SyntaxNode não é uma biblioteca de expressões regulares,
e sim a representação de nós da árvore sintática. System.SyntaxTrivia não é uma biblioteca de
expressões regulares, e sim a representação de trivialidades da linguagem de programação.
SyntaxToken_unit não é uma unidade de compilação, e sim os tokens da sintaxe, e Regex não é
o que a gramática gera ao ser compilada, e sim uma classe da linguagem C#.
System.SyntaxTrivia não é uma biblioteca de expressões regulares, e sim a representação de
trivialidades da linguagem de programação; e Regex não é o que a gramática gera ao ser
compilada, e sim um classe da linguagem C#. Por fim, SyntaxToken_unit não é uma unidade de
compilação, e sim os tokens da sintaxe.
3.
Uma gramática pode ser especificada com base em símbolos terminais e não terminais em sua
convenção. Com relação à classificação de símbolos de uma gramática, avalie as questões a
seguir e associe as colunas de acordo com as representações adotadas para símbolos terminais
e não terminais.
Resposta correta.
A.
I - 1; II - 2; III- 2; IV - 2; V - 1.
A associação correta é: I - 1; II - 2; III- 2; IV - 2; V - 1. Operadores - +, -, *, /, por exemplo, são
usados como símbolos terminais da gramática. Letras maiúsculas, como E e F, são usadas
como símbolos não terminais da gramática. Nomes em itálico formados com letras minúsculas,
como cmd ou expr, são usados como símbolos não terminais da gramática. A letra S representa
o símbolo de partida de uma gramática, sendo representado por símbolos não terminais. Letras
minúsculas do início do alfabeto, como a e c, são usadas como símbolos terminais da
gramática.
4.
Uma gramática pode produzir a mesma cadeia de caracteres de diferentes formas, gerando uma
ambiguidade. Considerando o contexto apresentado, avalie as seguintes asserções sobre a
ambiguidade da gramática livre de contexto:
I. A ambiguidade é gerada em uma gramática livre de contexto quando uma cadeia de
caracteres admite mais de um caminho ou mais de uma árvore sintática.
PORQUE
II. É interessante utilizar uma gramática sem ambiguidade em grande parte das aplicações.
Porém, em certas situações, é conveniente utilizar a ambiguidade levando em consideração
uma escolha criteriosa dessa ambiguidade.
A respeito dessas asserções, assinale a opção correta.
Resposta correta.
B.
As asserções I e II são proposições verdadeiras, mas a II não é uma justificativa da I.
As asserções I e II são proposições verdadeiras, mas a II não é uma justificativa da I. A
asserção I é verdadeira, pois uma gramática é considerada ambígua se existe uma mesma
sentença com duas ou mais derivações à direita ou à esquerda. A asserção II é verdadeira, uma
vez que em certas aplicações é interessante haver ambiguidade, porém ela deve ser escolhida
de forma criteriosa.
5.
As gramáticas livres de contexto (GLC) auxiliam nas construções das gramáticas atuais das
linguagens de programação e de compiladores. Seja L uma linguagem livre de contexto, com G
= ({S, T, B, C}, {b, c}, S, P}), em que:
S --> BT | TC
T --> bTc | Ɛ
B --> bB | b
C --> cC | c
e a cadeia bbbc. Assinale a alternativa que contém a derivação correta da linguagem livre de
contexto:
Resposta correta.
A.
S --> BT --> bBT --> bbT --> bbbTc --> bbbc.
Realizando as substituições com base na gramática, a derivação final é S --> BT --> bBT --> bbT
--> bbbTc --> bbbc. Nas derivações (S --> TC --> TCc --> bTCc --> bbTCb --> bbbc) não existe a
regra de produção C --> Cc. Nas derivações (S --> TC --> bTC --> bTCc --> bbTCc --> bbbc) não
existe a regra de produção C --> Cc. Nas derivações (S --> BTC --> bTC --> bTCc --> bcTCc -->
bbbc) não existe a regra de produção S --> BTC. Nas derivações (S --> BT --> cBT --> bcT -->
bcBc --> bbbc) não existe a regra de produção B --> cB.
1.
Conforme Cooper e Torczon (2014), antes que o compilador possa traduzir uma expressão para
código executável da máquina-alvo, faz-se necessário que ele entenda tanto a sua forma, ou
sintaxe, quanto o seu significado, ou semântica. Desse modo, o front-end determina se o código
de entrada está bem formado e, ao verificar que o código é válido, cria uma representação dele
na representação intermediária do compilador; em caso negativo, ele informa o usuário por meio
de mensagens de erros de diagnóstico com o intuito de possibilitar a identificação de problemas
no código.
Tendo em vista o apresentado, considere os códigos elaborados na linguagem Java
apresentados a seguir:
I. String nome = “compiladores”;
int num = 1;
System.out.print(nome / num);
II. int num;
num++;
III. int nome = “compiladores”;
IV. Strin endereço = “Rua...”;
V. int valores[] = new int[5];
valores[5] = 500;
Dados os códigos apresentados, faça a relação entre eles e as opções a seguir:
( ) A variável não foi inicializada.
( ) Referência desconhecida.
( ) Índice inválido.
( ) Os tipos não são compatíveis.
( ) O operador utilizado não suporta a operação.
A opção que representa a relação correta é:
Você acertou!
E.
II, IV, V, III, I.
Para o método de resolução, é preciso excluir as opções incorretas à medida que cada código
for analisado. Por fim, haverá apenas a opção correta:
I. String nome = “compiladores”;
int num = 1;
System.out.print(nome / num);
Note que, no código apresentado, há a tentativa da divisão, a partir do símbolo /, de valores do
tipo string com o tipo int; assim, o operador de \ não suporta a operação, pois não há a
possibilidade de tal divisão na linguagem Java e em demais linguagens fortemente tipadas.
Desse modo, é possível excluir as opções que não têm relação com o citado.
II. int num;
num++;
No segundo código apresentado, a variável num é criada e, em seguida, é incrementada, porém
não é inicializada. Desse modo, há um erro relacionado à variável não inicializada.
III. int nome = “compiladores”;
No terceiro código, é elaborada uma variável do tipo int, porém há a tentativa de inserção de um
valor string nela. Com isso, há um erro de tipos não compatíveis.
IV. Strin endereço = “Rua ...”;
Nesse caso, provavelmente houve erro de digitação, pois o tipo string é inserido faltando a letra
g, isto é, Strin. Nesse caso, há uma referência desconhecida, pois o compilador não reconhece
a palavra Strin.
V. int valores[] = new int[5];
valores[5] = 500;
Note que, no código apresentado, há a criação de um vetor denominado "valores" que tem cinco
posições. Na linguagem de programação Java, nesse caso, a primeira posição do vetor tem
índice 0, e a última, índice 4. Desse modo, não existe a posição 5 nesse vetor. Isso posto, há um
erro relacionado a índice inválido.
Assim, a opção correta é: II, IV, V, III, I.
2.
Uma gramática de atributos pode ser composta por atributos herdados ou sintetizados. No
primeiro caso, dada uma árvore de derivação, ela terá dependências de pai para filho ou de
irmão para irmão. No segundo caso, suas dependências estão relacionadas do filho para o pai.
No exemplo apresentado a seguir, todos os atributos são sintetizados, pois todas as
dependências da gramática estão relacionadas do filho para o pai. Nesse caso, a gramática é
S-atribuída.
S -> S @ A {S.valor = S.valor * A.valor}
S -> A {S.valor = A.valor}
A -> A $ B {A.valor = A.valor + B.valor}
A -> B {A.valor = B.valor}
B -> num {B.valor = num.valorlexico}
Dada a gramática apresentada anteriormente, qual o valor final de S.valor para uma cadeia
como 7 @ 5 $ 9 @ 7 $ 4?
Resposta correta.
D.
1078.
Uma das formas de verificar a opção correta é por meio da árvore de recorrência. Veja a árvore
a seguir:
3.
O processo de análise de um compilador pode ser dividido em três partes: análise léxica,
sintática e semântica. Na primeira, conforme Ricarte (2008), o compilador realiza uma análise a
partir da identificação dos caracteres individuais, dos símbolos básicos válidos para a
linguagem, entre outros. Na segunda, tenta compor, a partir da estrutura de cada um dos blocos
básicos, a estrutura geral do arquivo de entrada. Por fim, a terceira tem como objetivo buscar
verificar a coerência entre os fragmentos de códigos sintaticamente corretos.
Dado o exposto, analise o código a seguir, elaborado na linguagem Java:
public class Exercicio3 {
public static void main(String[] args) {
int min = 9;
int m@x = 11; // erro 1
Intervalo(min, m@x); // erro 2
}
public static void Intervalo(int min; int max) { // erro 3
int vetor[] = new int[max];
for (int i = min; i <= max; i++){
vetor[i] = i; // erro 4
System.out.println(i);
}
System.out.println(i); // erro 5
} // erro 6
Perceba que, para cada linha que tem determinado tipo de erro, existe um comentário
deixando-o em destaque e a sua numeração (erro 1, erro 2, ...).
Considerando os erros, marque a opção que representa apenas erros semânticos:
Você acertou!
B.
Erros 4 e 5.
Para responder esta questão, deve-se avaliar cada erro. Veja:
Erro 1:
int m@x = 11;
Note que, nesse erro, há uma variável denominada m@x. Nesse erro, o símbolo @ não é um
símbolo válido a ser utilizado em variáveis na linguagem Java. Portanto, trata-se de um erro
léxico.
Erro 2:
Intervalo(min, m@x);
Nesse código, é repetido o mesmo erro anterior, ao fazer uso da palavra m@x.
Erro 3:
public static void Intervalo(int min; int max) {
Perceba que, nesse código, há a utilização de um símbolo de ";" em vez de um símbolo de ",".
Desse modo, há um erro de sintaxe.
Erro 4:
vetor[i] = i;
Visivelmente esse código não está incorreto. No entanto, ao analisar que o vetor, nesse caso,
não tem a posição 11, que se refere ao valor max, contata-se, então, um erro semântico.
Erro 5:
System.out.println(i);
Novamente, ao analisar somente essa parte do código, não se torna visível qualquer erro. No
entanto, ao analisar uma parte maior do código, é possível perceber que a variável i não existe
no escopo no qual ela é utilizada. Desse modo, há um erro semântico.
Erro 6:
}
Nesse caso, há a falta de mais um símbolo de }. Portanto, temos um erro sintático.
Portanto, as opções que se referem a erros semânticos são os erros 4 e 5.
4.
Uma solução normalmente utilizada para especificar a semântica de determinada linguagem é
dada a partir da utilização de gramática de atributos. Esta consiste em atribuir ações semânticas
às produções de modo que, a cada produção executada, uma ação relativa a ela também seja
executada.
Considere que determinado desenvolvedor de compiladores elabore uma gramática de atributos
conforme apresentada a seguir:
S -> S * A
S -> A + S
S -> A
A -> A - B
A -> B
B -> dígito
Considerando a gramática elaborada pelo desenvolvedor, marque a opção verdadeira:
Resposta correta.
E.
- tem a maior precedência, e *, a menor.
Para verificar a opção correta desta questão, é preciso partir da solução de um problema que
contenha as três operações aritméticas apresentadas na gramática. Para isso, considere uma
entrada igual a 4 - 5 + 1 * 3.
Assim, a árvore sintática ficará da seguinte forma:
5.
Lemone (2010) apresenta que uma gramática de atributos é uma gramática livre de contexto
com a adição de atributos e regras de avaliação de atributos, chamadas de "funções
semânticas". Assim, uma gramática de atributos pode especificar a semântica e a sintaxe.
Desse modo, os atributos são variáveis para as quais os valores são atribuídos, e cada variável
de atributo está associada a um ou mais não terminais ou terminais da gramática. Quanto às
regras semânticas, Engelen (2018) apresenta que elas são usadas por um compilador para
impor a semântica estática e/ou para produzir uma árvore de sintaxe abstrata enquanto analisa
o fluxo de token.
Dado o exposto, analise a gramática a seguir e informe qual das opções representa um valor
final para S = 1700.
Produção Regra semântica
S -> S1.digito S.valor = S1.valor * 10 + digito.valor
S -> digito S.valor = digito.valor
digito -> 0 digito.valor = 0
digito -> 1 digito.valor = 1
digito -> 2 digito.valor = 2
digito -> 3 digito.valor = 3
digito -> 4 digito.valor = 4
digito -> 5 digito.valor = 5
digito -> 6 digito.valor = 6
digito -> 7 digito.valor = 7
digito -> 8 digito.valor = 8
digito -> 9 digito.valor = 9
Resposta correta.
E.
1700.
Como forma de reproduzir o resultado do processamento a partir de uma entrada igual a 1700, é
preciso considerar a seguinte árvore de derivação:
1.
Ao realizar a validação da semântica em uma linguagem de programação, as expressões
precisamretornar um valor verdadeiro ou falso de acordo com o resultado da sentença.
Dependendo da situação, é possível ter regras semânticas, verificadas em tempo de compilação
ou em tempo de execução do programa. Em relação ao conceito e às características da
semântica dinâmica, analise as afirmações a seguir.
I. Na semântica operacional, as modificações realizadas no estado
da máquina não servem para gerar o significado de uma instrução.
II. A criação de um procedimento que evidencia a exatidão
dos programas compõe a semântica denotacional.
III. O método utilizado para descrever o significado dos programas
com base na recursividade é a semântica denotacional.
IV. A semântica operacional natural esconde detalhes ao realizar
 um passo computacional maior em um programa.
Agora, assinale a alternativa que apresenta a resposta correta.
Você acertou!
C.
Apenas as afirmativas III e IV estão corretas.
I. A afirmativa está incorreta, pois a semântica operacional descreve o comportamento de um
programa ao ser executado em um computador real ou hipotético. As modificações no estado da
máquina, como a localização de memória e os valores dos registradores, determinam o
significado de uma instrução.
II. A afirmativa está incorreta, já que a semântica axiomática foi definida com a criação de um
método que comprova a exatidão dos programas que exibem a computação descrita por sua
especificação, quando esta pode ser desenvolvida.
III. A afirmativa está correta, pois a semântica denotacional é baseada na teoria da função
recursiva.
IV. A afirmativa está correta, pois a semântica natural torna mais simples as notações e esconde
detalhes ao realizar um passo computacional maior (big_step), entre o estado inicial e o estado
final de um programa.
2.
Ao executar um programa na linguagem Pascal, o compilador, após verificar a sintaxe, pode
sinalizar os erros semânticos. Observe o código a seguir:
var x: String;
 x := 3 + ‘7’;
Assinale a alternativa correta que apresenta o erro semântico encontrado neste código.
Você acertou!
A.
Erro de compatibilidade de tipos de dados.
O erro semântico do código é o erro de compatibilidade de tipos de dados, uma vez que foi
atribuído a uma variável do tipo string, uma operação matemática.
Não é possível ser erro de posição inexistente em um vetor, uma vez que o trecho de programa
não trata sobre vetores; além disso, a variável foi declarada e está dentro do escopo correto do
programa.
3.
A semântica denotacional constitui o método mais rígido para descrever o significado dos
programas, sendo baseado na teoria
da função recursiva. Considerando o contexto apresentado, avalie
as seguintes asserções sobre a semântica denotacional.
I. A semântica denotacional utiliza uma função que estrutura a
 entidade e o objeto matemático.
PORQUE
 II. O objeto matemático representa o significado das entidades sintáticas correspondentes.
A respeito dessas asserções, assinale a opção correta.
Resposta correta.
A.
As asserções I e II são proposições verdadeiras, e a II é uma justificativa da I.
I. Asserção verdadeira, pois a semântica denotacional define para cada entidade da linguagem
um objeto matemático, como uma função que faça o mapeamento da entidade e do objeto
matemático.
II. Asserção verdadeira e justifica a I, uma vez que o método é chamado de denotacional,
porque os objetos matemáticos representam o significado das entidades sintáticas
correspondentes.
4.
Ao executar um programa, o compilador, após verificar a sintaxe,
 pode sinalizar os erros semânticos. Um erro semântico ocorre quando um programa está certo
sintaticamente, mas o resultado gerado não está correto. Analise o seguinte trecho do programa
escrito em C#:
public class Teste : Teste
{
int k = “xyz” * false;
int y = 1.22;
void x(int c, int c)
{
int a;
int p = 14 * a;
}
}
Em relação aos erros semânticos apresentados no trecho do programa acima, analise as
afirmações a seguir.
I. Não é possível uma classe herdar de si mesma — class Teste : Teste.
II. Não é possível multiplicar uma string por um boolean.
III. O número 1.22 não pode ser atribuído como um inteiro.
IV. Na instrução int p = 14 * a, do método x, a variável a não foi inicializada.
 V. Não é possível declarar parâmetros com o mesmo nome e com o mesmo tipo.
Agora, assinale a alternativa que apresenta a resposta correta.
Resposta correta.
E.
Todas as afirmativas estão corretas. 
I. A afirmativa está correta, pois não é possível uma classe herdar de si mesma e, para que a
classe esteja correta, é preciso alterar o nome da classe herdada como class Teste : TesteNovo.
II. A afirmativa está correta, visto que não é possível multiplicar uma string por um boolean,
sendo necessário corrigir a expressão para comparar um tipo booleano com outro tipo booleano,
por exemplo.
III. A afirmativa está correta, pois o número 1.22 não pode ser atribuído como um inteiro, sendo
necessário alterar o valor de y = 1, por exemplo.
IV. A afirmativa está correta, pois, na instrução int p = 14 * a, do método x, a variável a não foi
inicializada, por exemplo, a = 10.
V. A afirmativa está correta, já que não se pode declarar parâmetros com o mesmo nome e com
o mesmo tipo, sendo necessário renomear o parâmetro void x(int c, int d).
5.
As regras da semântica dinâmica são classificadas em operacional, denotacional e axiomática.
Com relação à classificação da semântica dinâmica, avalie as questões a seguir e associe os
itens numerados de acordo com as representações adotadas para semântica operacional,
denotacional e axiomática.
1. Semântica operacional
2. Semântica axiomática
3. Semântica denotacional
I. Utiliza expressões lógicas, conhecidas como asserções.
II. É um método representado por objetos matemáticos
definindo o significado dos atributos sintáticos.
III. Esse método é baseado em funções recursivas.
IV. É utilizada em manuais das linguagens e livros didáticos.
 V. Esse método é classificado em natural e estrutural.
Assinale a alternativa que apresenta a associação correta
 entre as colunas.
Você acertou!
A.
I – 2; II – 3; III – 3; IV – 1; V – 1.
A associação correta é: I – 2; II – 3; III – 3; IV – 1; V – 1.
A semântica axiomática utiliza expressões lógicas chamadas de asserções ou predicado, que
definem as restrições a variáveis. O método é chamado de denotacional porque os objetos
matemáticos representam o significado das entidades sintáticas correspondentes. A semântica
denotacional pode ser usada para provar a correção de um programa, por meio de um método
para pensar sobre o programa, além de descrever uma linguagem precisa. A semântica
operacional é pouco utilizada pelos desenvolvedores da linguagem e classifica-se em natural,
que esconde detalhes ao realizar um passo computacional maior (big_step), e estrutural, que
define detalhes da execução ao tomar um passo menor (small_step).
UNIT 3
1.
Os compiladores são organizados em uma sequência de fases. À medida que o compilador
deriva conhecimento sobre o código que compila, precisa transmitir essa informação de uma
fase para outra. Ele necessita, então, de uma representação para todas as informações que
deriva sobre o programa-fonte. Chama-se isso de representação intermediária (IR).
Sobre as IRs geradas por um compilador, considere as seguintes afirmações:
I — A árvore sintática é a forma de representação intermediária mais próxima da estrutura do
código-alvo.
II — A árvore sintática abstrata é uma representação contendo mais informações sobre as
produções gramaticais realizadas, se comparada com a árvore sintática comum.
III — Um dos motivos de se utilizar o código de três endereços como representação
intermediária é a sua facilidade de tradução para linguagens de máquina.
Assinale a alternativa que indica as afirmações corretas:
Você acertou!
C.
Apenas a afirmação III está correta.
Sobre as afirmações feitas, apenas a afirmação III é correta. O código de três endereços é muito
parecido com os tipos de operaçãoem linguagem Assembly, o que o torna uma boa forma de
código intermediário. Assim, um dos motivos de se utilizar o código de três endereços como IR é
a sua facilidade de tradução para linguagens de máquina. A afirmação I é incorreta, pois uma
árvore sintática é uma representação do código-fonte, e não do código-alvo. A afirmação II
também está incorreta, pois é justamente o contrário: a árvore sintática comum tem mais nós
relativos aos símbolos das produções gramaticais do que a árvore sintática abstrata, que
representa apenas os símbolos terminais usados nas produções.
2.
A função de um compilador é traduzir programas escritos em uma linguagem-fonte em um dos
programas equivalentes em outra linguagem-alvo. Para isso, o compilador é dividido em fases,
que implementam diversas técnicas.
A ___________________ é uma técnica de tradução aplicada em compiladores que consiste
em inserir regras de tradução nas produções gramaticais da linguagem-fonte, de modo que o
percurso na árvore sintática executa a tradução desejada.
Qual das alternativas a seguir preenche melhor a lacuna na afirmação?
Você acertou!
D.
Tradução dirigida pela sintaxe.
A frase correta é: A tradução dirigida pela sintaxe é uma técnica de tradução aplicada em
compiladores que consiste em inserir regras de tradução nas produções gramaticais da
linguagem-fonte, de modo que o percurso na árvore sintática executa a tradução desejada.
A análise léxica consiste em identificar tokens da linguagem-fonte do texto do programa-fonte.
Os tokens reconhecidos são usados na análise sintática para validar essa sequência com a
gramática da linguagem-fonte. Entretanto, nenhuma tradução é feita. A otimização de código
consiste em tornar o código-alvo mais eficiente, mas isso independe da linguagem-fonte. A
interpretação de código consiste em executar instruções na máquina-alvo a partir da análise das
instruções do código-fonte.
3.
Compiladores podem gerar uma variedade de representações intermediárias para representar
as construções sintáticas da linguagem-fonte. Cada representação tem a finalidade de registrar
informações de uma fase da compilação para as fases seguintes.
___________________ é uma representação intermediária que consiste em representar as
produções gramaticais de forma mais sucinta, de modo que apenas os símbolos terminais das
produções gramaticais compõem suas construções.
Qual das opções a seguir completa apropriadamente a lacuna da afirmação?
Resposta correta.
B.
Árvore sintática abstrata.
A árvore sintática abstrata é uma representação intermediária que consiste em representar as
produções gramaticais de forma mais sucinta, de modo que apenas os símbolos terminais das
produções gramaticais compõem suas construções.
Ela é uma versão resumida da árvore sintática. A árvore sintática também representa símbolos
não terminais em suas construções. A árvore sintática anotada consiste em uma árvore sintática
contendo em suas ramificações regras de tradução, além de terminais e não terminais. Um grafo
de dependências é um grafo que modela o fluxo de valores desde suas definições até seus usos
em um fragmento de código. O código de três endereços é uma representação de código
intermediário já voltada para geração de linguagem-alvo.
4.
Na notação tradicional para as expressões aritméticas (notação infixa), o operador aparece
entre os seus dois operandos. Já na notação pós-fixa, também conhecida como notação
polonesa inversa, o operador é colocado após os seus dois operandos.
Considere o esquema de tradução de expressões para a forma pós-fixa para a gramática a
seguir:
EXPR → EXPR + TERMO { imprimir (“+”) }
EXPR → EXPR – TERMO { imprimir (“-”) }
EXPR → TERMO
TERMO → TERMO * FATOR {imprimir (“*”) }
TERMO → TERMO / FATOR {imprimir (“/”) }
TERMO → FATOR
FATOR → ( EXPR )
FATOR → 0 { imprimir (“0”) }
... ...
FATOR → 9 { imprimir (“9”) }
Qual será o resultado da sua execução para a entrada 2 + 4 * (5 - 3)?
Você acertou!
D.
2 4 5 3 - * +.
A árvore sintática anotada para a entrada 2 + 4 * (5 - 3), usando a gramática proposta, é
mostrada na figura abaixo:
5.
As fases de um compilador podem ser classificadas em dois grupos: análise e síntese. Na fase
de análise, encontram-se as fases de análise léxica, análise sintática e análise semântica. Já na
fase de síntese, encontram-se as fases de geração de código intermediário, otimização e
geração de código.
Sobre a fase de geração de código intermediário de um compilador, assinale a alternativa
correta:
Você acertou!
C.
A fase de geração de código intermediário de um compilador é a fase em que ocorre a
transformação da árvore sintática em uma linguagem intermediária, que é mais próxima da
linguagem-objeto do que o código-fonte, mas ainda não se trata do código-final.
Análise léxica é a fase em que se reconhecem os tokens da linguagem-fonte por meio da leitura
dos caracteres do código-fonte.
Análise sintática é a fase em que se constrói uma árvore sintática para validar a estrutura dos
tokens reconhecidos do código-fonte.
Geração de código intermediário é a fase em que ocorre a transformação da árvore sintática em
uma linguagem intermediária, que é mais próxima da linguagem-objeto do que o código-fonte,
mas ainda não se trata do código final.
Otimização de código é a fase em que se procura produzir um código que execute com mais
eficiência.
Geração de código final é a fase em que se gera o código para ser executado diretamente na
máquina-alvo.
1.
A otimização do código deve ser capaz de manter o mesmo resultado, ainda que partes do
código sejam eliminadas, substituídas ou modificadas.
Ainda assim, durante a otimização deverá ser mantido(a):
Resposta correta.
B.
o significado semântico.
O significado semântico não deverá ser alterado, já que o código deverá cumprir o mesmo
resultado. A sequência das instruções pode ser alterada a fim de aproveitar melhor os
registradores e outros motivos. As instruções utilizadas nem sempre são as mais adequadas.
Muitas vezes um processador pode disponibilizar instruções específicas para um tipo de
problema que será solucionado de maneira mais eficaz. Os registadores alocados não farão
diferença se o significado semântico e os resultados obtidos forem os mesmos e podem ser
alterados para reduzir a necessidade de carregamentos e armazenamentos em memória. As
expressões podem ser alteradas por diversos motivos: simplificação do cálculo, aproveitamento
de expressões comuns, uso de recursos específicos em determinados processadores.
2.
A otimização pode ser empregada em diferentes etapas da compilação, podendo ser técnicas
dependentes ou independentes de máquina.
Qual das técnicas abaixo é classificada como dependente de máquina?
Você acertou!
A.
Otimização de pequena escala.
A otimização de pequena escala busca por pequenos trechos de código de máquina que podem
ser simplificados, sem se preocupar com o contexto maior. Essa técnica requer o conhecimento
dos recursos disponibilizados pela máquina.
A eliminação de código morto é realizada diretamente sobre o código intermediário e independe
de máquina.
A movimentação de código analisa o código dentro de laços e blocos que são frequentemente
utilizados e independe de máquina.
A avaliação em tempo de compilação busca elementos ou expressões constantes que podem
ter seu valor substituído durante a compilação, já que não serão alterados durante a execução,
e independe de máquina.
A análise léxica é parte do processo de compilação, mas não consiste em uma técnica de
otimização.
3.
Algumas técnicas de otimização de código podem simplificar trechos tão pequenos de código
como uma única expressão.
Qual a técnica de otimização de código utilizada no exemplo a seguir?
Código original:
x = a / 4;
Código otimizado:
x = a >> 2;
Você acertou!
D.
Redução de força.
A expressão foi alterada substituindo o operador utilizado por um que facilitasse o cálculo. Já
que 4 é um múltiplo binário, pode-se efetuar uma divisão por esse valor movimentando todos os
bits da variável a duas casas para a direita. Assim, a técnicautilizada foi de redução de força.
4.
Na propagação de variável, elementos comuns entre expressões podem ser utilizados para
eliminar trechos de código desnecessários ou reduzir expressões.
Utilizando a técnica de propagação de variável, assinale a alternativa que apresenta a solução
correta de otimização para o código a seguir:
a = b + c;
x = a;
d = x + e;
Resposta correta.
C.
a = b + c;
d = a + e;
A variável x recebe o valor de a após a expressão a = b + c. No momento em que a expressão d
= x + e é executada, o valor de x e a são os mesmos, podendo substituir x nessa expressão pelo
próprio valor de a. Por fim, x = a pode ser eliminado por não ter mais serventia para o código.
Assim, d = b + c + e; não pode ser utilizado porque o valor de a consiste em um valor diferente
das demais variáveis e que pode ser utilizado em outra parte do código, o que não acontecia
com x que terá sempre o mesmo valor de a.
5.
As técnicas de otimização dependentes de máquina lidam diretamente com o código final, em
que o código fonte já foi traduzido para um conjunto de instruções compreensíveis por ela.
Dado o código a seguir, assinale a alternativa que apresenta uma simplificação válida utilizando
a técnica de alocação e atribuição de registradores, considerando as variáveis x e y como
necessárias no restante do código.
Considere as seguintes instruções:
LD: carrega um valor em um registrador
ST: armazena um valor em uma variável
DEC: decrementa um registrador
ADD x, y, z: efetua uma soma: x = y + z
SUB x, y, z: efetua uma subtração: x = y - z
LD R0, 5
ADD R0, R0, b
ST x, R0
LD R0, x
SUB R0, R0, c
ST y, R0
LD R0, y
DEC R0
ST y, R0
2 4 5 3 - * +
Resposta correta.
A.
LD R0, 5
ADD R0, R0, b
ST x, R0
SUB R0, R0, c
DEC R0
ST y, R0
Não há necessidade de utilizar um segundo registrador (R1).
Se decrementar R0 sem armazenar antes o resultado de R0-c no próprio R0, o resultado será
diferente do original.
Ao aproveitar sempre o registrador R0, os armazenamentos e carregamentos de R0 novamente
são desnecessários enquanto a conta prossegue.
Ainda assim, o armazenamento (ST) de x e y, como descrito no código, é necessário, já que
serão valores buscados no resto do programa.
Logo, pode-se evitar o uso de R1e das instruções de carregamento (LD), mas não das
instruções de armazenamento (ST).
Assim, a alternativa correta é:
LD R0, 5
ADD R0, R0, b
ST x, R0
SUB R0, R0, c
DEC R0
ST y, R0
1.
A tarefa de otimização de código traz à tona diversas questões que devem ser levadas em
consideração. Inúmeros fatores podem contribuir na complexidade das análises realizadas
quanto ao fluxo de dados e de controle na otimização. Por isso, definir as melhores técnicas a
serem utilizadas nem sempre é uma tarefa fácil. Além disso, sempre é preciso ficar atento ao
objetivo final do programa quando se está realizando a otimização de código.
Leia as afirmações a seguir sobre os cuidados que devem ser tomados no momento de realizar
as otimizações nos códigos:
I. Modificações que possam ter impacto prejudicial no código objeto devem ser evitadas, pois a
intenção da otimização é melhorar o desempenho do código, conservando a essência do
programa.
II. As trocas de comandos (como realizar a transformação de while pelo if) nos códigos
intermediários devem ser evitadas, pois podem prejudicar o significado dos códigos originais.
III. O empenho aplicado para otimizar os códigos deve ser satisfatório e estimável, ou seja, algo
que possa ser medido, garantindo otimização adequada e satisfatória.
Assinale a alternativa que contém as assertivas corretas:
Resposta correta.
C.
apenas I e III.
As afirmações I e III estão corretas, pois tratam de questões pertinentes aos cuidados que
devem ser tomados no momento de aplicar as otimizações, como preservar o significado do
programa original e medir o quanto a otimização pode ser eficiente. Já a afirmação II não está
correta, pois utiliza um exemplo que não se aplica na situação de transformações dos códigos
intermediários, tendo em vista que a troca dos comandoswhile pelo ifpode ocorrer no momento
da geração de códigos intermediários para realizar otimizações.
2.
A otimização, levando em consideração a análise de fluxo de controle, é responsável pelo
comportamento do código em relação aos laços e às repetições, algo crucial no momento de
otimizar os códigos intermediários. As análises podem ser realizadas pelos blocos básicos de
forma global ou local, e a representação da estrutura de um código pode ser realizada por meio
de grafos de fluxo de controle.
Uma das técnicas que podem ser aplicadas na análise de fluxo de controle é a
_________________, que apresenta como objetivo realizar a transformação de variáveis que
mudam constantemente a cada iteração. Esse tipo de variável pode ser encontrado nos acessos
a vetores e matrizes.
Leia as alternativas e assinale a que preenche a lacuna de forma correta:
Resposta correta.
B.
simplificação de variáveis de indução.
Quando são utilizadas propriedades algébricas na otimização, a redução de capacidade realiza
substituições de operadores de alto custo por operadores mais simples. As variáveis de indução,
da simplificação de variáveis de indução, mudam constantementeem cada iteração, pois estão
presentes, geralmente, nos acessos a vetores e matrizes e, assim, podem ser transformadas.
Assim, uma das técnicas que podem ser aplicadas na análise de fluxo de controle é a
simplificação de variáveis de indução. Quando um bloco de instruções não tem a possibilidade
de ser executado, é possível aplicar a eliminação de código morto. Expressões que estão
presentes nos laços e não se modificam ao longo da execução podem ser analisadas pela
técnica de movimentação de código. E a eliminação de desvios desnecessários trata de
instruções de desvios que não são necessários, pois geralmente não são executados e, assim,
podem ser eliminados.
3.
Um bloco básico pode ser analisado de forma local ou global quando se está realizando
otimização no código. A análise de fluxo de dados procura verificar variáveis e expressões
presentes nos blocos básicos a fim de aplicar técnicas para melhorar o desempenho.
Assinale a alternativa correta quanto à análise de fluxo de dados.
Você acertou!
D.
A análise de variáveis diferentes, mas que têm valores iguais, pode ser realizada, tendo em vista
que essas variáveis podem ser eliminadas posteriormente, contribuindo na otimização.
A eliminação de expressões repetidas pode ser definida como a técnica de eliminação de código
redundante, mas ela não pode ser eliminada de imediato, pois primeiramente a análise de fluxo
de dados verifica se essa expressão não é utilizada posteriormente com outros valores
atribuídos. A comunicação entre os blocos básicos é permitida pelas variáveis e expressões dos
conjuntos de entrada (in) e saída (out) entre os blocos básicos, e não os predecessores (pred) e
seus sucessores (succ). Os grafos acíclicos dirigidos (DAG) têm como foco analisar a sequência
das instruções, facilitando a descoberta de subexpressões comuns, por exemplo. As expressões
chamadas de available não precisam ser recalculadas, pois estão sempre disponíveis. A
propagação de cópias é a técnica que verifica que variáveis diferentes são utilizadas com os
mesmos valores e, assim, podem ser eliminadas posteriormente. Assim, a análise de variáveis
diferentes, mas que têm valores iguais, pode ser realizada, tendo em vista que essas variáveis
podem ser eliminadas posteriormente, contribuindo na otimização.
4.
A análise de fluxo de controle e de dados na otimização de códigos intermediários pode ser feita
de maneira conjunta para melhorar o desempenho, sempre visando a contribuir positivamente
na geração do código, tendo como princípio a aplicação de diferentes técnicas para tal.
Observe a expressão a seguir em linguagem C:
y[i] = x[i] * x[i-1]
Levando em consideração que todas as variáveis são do tipo inteiro, o bloco de instruções em
código intermediário de três endereços ficaria desta forma:
Linha 1 ➔ _t1 := 4 * i
Linha 2 ➔ _t2 := i - 1
Linha3 ➔ _t3 := 4 * _t2
Linha 4 ➔ _t4 := 4 * i
Linha 5 ➔ y[_t4] := x[_t1] * x[_t3]
Assinale a alternativa correta que apresenta as técnicas que podem ser aplicadas para otimizar
esse trecho de código.
Você acertou!
B.
Identidades aritméticas e eliminação de subexpressões comuns.
Nesse trecho de código, a técnica de movimentação de código não pode ser aplicada, pois não
são expressões que estão presentes em laços. A eliminação de desvios desnecessários trata de
instruções de desvios, não sendo adequada a esse trecho de código. A eliminação de código
morto também não pode ser aplicada, tendo em vista que se refere a um bloco de instruções
que não tem a possibilidade de ser executado. Entre as propriedades algébricas está a redução
de capacidade, que troca operadores mais complexos por mais simples, mas, nesse caso, não
há essa possibilidade.
Para realizar a otimização do trecho de código, as técnicas que podem ser aplicadas são
identidades aritméticas(linha 2) e eliminação de subexpressões comuns (linhas 1 e 4).
Observe como fica o trecho de código intermediário otimizado:
_t1 := 4 * i
_t2 := _t1 - 1
y [_t1] := x[_t1] * x[_t2]
5.
O grafo de fluxo de controle é uma ferramenta muito importante na análise de fluxo de controle,
pois possibilita não só realizar uma representação gráfica da estrutura dos blocos básicos, mas
também representa as dependências entre eles. A partir disso, a aplicação de técnicas pode ser
facilitada nos códigos intermediários.
Leia as afirmativas a seguir, que tratam da construção dos grafos de fluxo de controle, e
classifique-as em verdadeiras (V) ou falsas (F).
( ) Cada bloco básico pode ser atribuído a uma aresta dirigida que representa as instruções de
saltos para outros vértices.
( ) Saltos condicionais e incondicionais apresentam duas arestas (condições verdadeiras ou
falsas), mas, quando não há saltos de instruções, é atribuída apenas uma aresta.
( ) Os vértices representam cada bloco básico, e as arestas, as instruções de saltos (podendo
ser condicional ou incondicional).
Assinale a alternativa que apresenta a ordem correta das afirmativas:
Você acertou!
C.
F – F – V.
Para a construção dos grafos de fluxo de controle, é necessário levar em consideração que os
vértices representam cada bloco básico, e as arestas, as instruções de saltos (podendo ser
condicional ou incondicional). Quando há saltos condicionais, duas arestas são lançadas
(condições verdadeiras ou falsas), mas, se os saltos são incondicionais ou sem saltos, há
apenas uma aresta. Os vértices que não têm arestas representam blocos com instruções de
retorno de funções.
1.
Gerir os recursos disponibilizados pela máquina é um processo que demanda habilidade tanto
do desenvolvedor quanto da eficiência do compilador.
A alocação de memória, qualquer que seja consiste em:
Você acertou!
E.
atribuir espaço suficiente e o tipo de alocação mais adequado para cada variável.
A reserva de espaço extra pode ocorrer em variáveis cujo tamanho é desconhecido, embora a
maioria delas já tenha um tamanho definido. A alocação de memória pode lidar com o código a
ser executado, mas para alocá-lo na memória RAM em tempo de execução, enquanto a posição
em um disco rígido é atribuição do sistema operacional. A seleção de registradores diz respeito
ao processo de otimização do código. A eliminação de objetos não mais referenciados faz parte
do processo de coleta de lixo na memória heap, mas que não está necessariamente presente,
tampouco integrando outras formas de alocação. No entanto, para todos os casos, é necessário
encontrar a quantidade de memória suficiente para armazenar um dado e se este poderá ou não
ser alterado — não podendo ser estático —, além do tempo de vida para determinar entre as
modalidades de alocação dinâmica.
2.
As variáveis alocadas estaticamente não podem ter seus valores alterados.
Assinale a alternativa que cita uma vantagem da alocação de memória do tipo estático.
Resposta correta.
A.
Velocidade de acesso.
Por serem fixas e permanentes, espera-se um maior uso de memória na alocação estática. A
alocação não é feita em tempo de execução, e sim durante a compilação. Os endereços, uma
vez definidos, não serão alterados, tampouco o tamanho da memória alocada. A velocidade de
acesso é maior, já que os endereços são fixos e previamente conhecidos, constituindo esta uma
vantagem.
3.
Na alocação de memória dinâmica, o tamanho e o endereço a ser utilizado somente serão
conhecidos pelo sistema no momento da execução.
Assinale a alternativa que apresenta uma desvantagem desse tipo de alocação.
Resposta correta.
A.
Requer mecanismo de limpeza.
A alocação dinâmica pode ocorrer de duas formas: pilha ou heap. Os dados são alocados em
tempo de execução, o que torna a compilação mais rápida. É possível aumentar o tamanho de
uma variável utilizando a alocação heap. Uma vez que muitas variáveis podem ser criadas em
tempo de execução, é necessário implementar um sistema de limpeza, a fim de liberar espaço
sempre que variáveis anteriormente alocadas se tornem inúteis, sendo esta uma desvantagem.
4.
A alocação de memória dinâmica pode ser feita em modo pilha (stack) e em modo heap
(monte). Apesar de não existir uma regra única, existem situações em que determinado tipo é
mais benéfico que o outro.
A pilha é a principal implementação quando se dá a alocação em virtude da(do):
Você acertou!
C.
chamada de uma função.
Valores constantes podem ser armazenados na memória estática, liberando espaço na memória
dinâmica para outras variáveis, além de aproveitarem a maior velocidade de acesso nesse caso.
Dentro da pilha, não é possível alterar o tamanho de uma variável em virtude do mecanismo de
LIFO. Variáveis globais são utilizadas em diferentes escopos e não podem ser eliminadas, como
no sistema LIFO. O carregamento do programa na memória durante a inicialização faz uso da
memória estática, uma vez que é mais rápida e o programa não pretende ser alterado em tempo
de execução. Já durante a chamada de uma função, espera-se que variáveis necessárias
somente dentro daquele escopo sejam utilizadas, como os argumentos e dados de retorno, que
poderão ser eliminados da memória tão logo a função retorne.
5.
Normalmente, a pilha apresenta um tamanho previamente definido e que, se ultrapassado,
resultará em uma exceção conhecida como stack overflow (ou “estouro de pilha”).
Qual das alternativas a seguir apresenta uma forma de reduzir as ocorrências de stack
overflow?
Você acertou!
A.
Manter somente a referência ao objeto na stack.
Ao fazer uso de argumentos de tamanho flexível, caso o sistema tente usar a pilha para essa
finalidade, pode ser alocado um espaço menor que o necessário e, quando a variável aumentar,
ocorra a sobreposição de dados, podendo inclusive superar o próprio tamanho da pilha. Os
argumentos das funções costumam ser armazenados em uma pilha junto a outros valores, como
os dados temporários, os endereços de referência para retorno, os valores de retorno, entre
outros. Assim, a quantidade e o tamanho desses argumentos impactam diretamente no tamanho
da pilha. Chamadas consecutivas, sem retorno, seja por instrução recursiva, seja por
aninhamento de múltiplas funções, podem fazer com que a pilha precise armazenar mais dados
que o comportado. Para cada chamada, um novo conjunto de dados é armazenado, o qual
somente será eliminado ao retornar da função. Assim, variáveis muito grandes devem ser
passadas como argumento, e a pilha pode ser utilizada para armazenar apenas um ponteiro
para essa variável e alocar os dados em si na memória heap. Portanto, uma maneira de reduzir
as ocorrências de stack overflow consiste em manter somente a referência ao objeto na stack.
UNIT 4
1.
Basicamente, há duas fases de compiladores, e cada uma delas cria uma representação do
código original mais próxima do código que a máquina consegue executar.
Considerando o momento em que a síntese é realizada e seus objetivos, assinale a alternativa
correta.
Resposta correta.
B.
A síntese é realizada após os

Outros materiais