Mudanças entre as edições de "Sistemas Operacionais e Introdução a Programação (diário 2009-2)"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
Linha 1 276: Linha 1 276:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
é o início de praticamente qualquer programa em C, uma vez que contém a função principal, <tt>main()</tt>. Neste caso, definida com um conjunto vazio de instruções - limitada pelas chaves.
+
é o início de praticamente qualquer programa em C, uma vez que contém a função principal, <tt>main()</tt>. Neste caso, definida com um conjunto vazio de instruções - limitado pelas chaves.
  
 
==16/12: Recuperação==
 
==16/12: Recuperação==

Edição das 13h45min de 9 de novembro de 2009

Sistemas Operacionais

27/07: NÃO HOUVE

29/07: NÃO HOUVE

03/08: NÃO HOUVE

05/08: NÃO HOUVE

10/08: História dos Sistemas Operacionais e das Linguagens de Programação

12/08: Usuários e Grupos

Atividades

  • Quem pode executar o comando /usr/bin/firefox? O usuário aluno pode modificar tais permissões?
  • Para o comando acima mencionado, informe quais as permissões e propriedades necessárias para permitir que apenas usuários do grupo "internet" possam executá-lo.
  • Se um dado arquivo tiver as permissões 000 (formato octal de representação), mesmo assim o usuário root tem permissão de leitura. Por quê?
  • A partir do diretório raiz (/), quais são todos os diretórios em que o usuário aluno pode criar arquivos e subdiretórios? Para cada caso, informe que propriedades e permissões garantem tal direito.
  • Um certo programa, chamado firefox-3.6, foi descarregado da Internet pelo usuário aluno, mas ele não pode ser executado. Que permissões garantem que ele possa ser executado por tal usuário?
  • Qual a lista completa de usuário e grupos do sistema operacional Linux? A resposta está em dois arquivos que utilizamos como exemplo... eles estão no diretório /etc.

17/08: Processos

  • Tópicos: árvore de processos, escalonamento de processos e seus estados.
  • Referência externa: Cap. 2 do livro Sistemas Operacionais Modernos - particularmente os tópicos vistos em aula.

19/08: Sistemas de Arquivos

  • Tópicos: arquivos e diretórios, hard link e soft link, sistema de arquivos corrente e pontos de montagem.
  • Referência externa:

Atividades

  • Quantos processos chamados bash estão rodando no sistema?
  • Se todos os processos bash forem encerrados, todas as sessões de usuário (logins) também o serão? Por quê?
  • O comando top pode mostrar todos os processos de um certo usuário, como por exemplo aluno? O comando ps também? Por que um processo precisa ter um usuário associado a ele?
  • É possível identificar, através do comando top, quais processos estão em estado bloqueado?
  • É possível criar um soft link de outro soft link? Eles conterão o mesmo conteúdo?
  • Se o conteúdo de um certo arquivo é acessado pelo seu soft link, por que este arquivo (original) tem a data de modificação alterada?
  • Um arquivo pode ter, como conteúdo, um sistema de arquivos? Explique como isso é possível, e qual o processo de ler este conteúdo.
  • Crie um soft link com o nome /tmp/diretorioNovo/arquivoNovo para o arquivo /etc/passwd. Em caso de dúvida, consulte a lista de comandos.
  • É possível bloquear o uso do comando ls - armazenado no diretório /bin - apenas retirando as permissões de execução? Por quê? Exemplifique com o usuário aluno.

24/08: Linguagens de Programação

  • Tópicos: linguagens de programação, execução de programas.

26/08: Revisão de Sistemas Operacionais

Assumindo como cenário o sistema operacional GNU/Linux para auxiliar nas respostas.

  • Por que as permissões em arquivos são tão importantes?

Pelo fato de o sistema prover um ambiente multi-usuário, é preciso que haja limites para cada um dos usuários desse sistema, não só para controlar o acesso a arquivos pessoais mas também a informação referente ao próprio S.O. - acesso ao arquivo de senhas, por exemplo.

Comandos relacionados: ls.

  • Como se dá o controle de acesso a arquivos abertos por processos?

As permissões a arquivos, sejam eles executáveis ou não, se dão por uma lista de controle de acesso: usuário (dono do arquivo), grupo e outros. Se o usuário possui a permissão por ele desejada (leitura, escrita ou execução), terá o respectivo acesso ao arquivo. Isso acontecerá para um executável (permissão de execução), por exemplo. E como um processo está sempre associado a um usuário (ou por padrão outorgados ao root), ainda assim tem-se a relação usuário-permissões, as quais serão utilizadas para abrir arquivos. Exemplo: o comando cat que lerá o arquivo /etc/passwd

Comandos relacionados: lsof, fuser.

  • Todo processo tem um dono? Por quê?

Todo arquivo tem um dono, e todo processo também. Parte desta resposta já fora apresentada na questão anterior, mas cabe complementar: é preciso estabelecer limites para os usuários e seus processos e arquivos, a fim de que não haja abuso ou mesmo a inoperância do sistema como um todo por causa de falhas humanas e/ou má fé.

Comandos relacionados: ps, top.

  • Quantos e quais são os estados de um processo? O que um processo precisa fazer para entrar para o estado bloqueado?

<graphviz> digraph Estados { Criação Apto Executando [shape=record] Bloqueado Destruição

Criação -> Apto [label="Admissão"] Apto -> Executando [label="Seleção"] Executando -> Apto [label="Int. por tempo/voluntária"] Executando -> Bloqueado [label="Sincr. ou req. de E/S"] Bloqueado -> Apto [label="Ocorrência de evento"] Executando -> Destruição [label="Término"] } </graphviz>

São cinco os estados, conforme o material recomendado pelo Prof. Semprebom. No caso, processos que estão no estado executando podem fazer uso de I/O bound, o que significa que haverá mais E/S e menos processamento. Por este motivo, é interessante que este processo fique aguardando o seu recurso (via E/S), enquanto que outros processos, que fazem mais CPU bound, aproveitem melhor o recurso de processamento.

Comandos relacionados: ps, top.

  • Qual a relação entre escalonamento preemptivo e prioridade de processos?

Associado ao escalonamento preemptivo, faz-se o uso de prioridades para garantir que os processos sejam atendidos conforme a sua demanda: processos referentes a aplicações de tempo real, por exemplo, requerem mais processamento, portanto uma maior prioridade. Contudo, é preciso, ao longo do tempo, manipular tais prioridades, garantindo que os todos os processos rodem - caso contrário aqueles com maior prioridade sempre voltarão ao começo da fila. O envelhecimento de prioridades, portanto, é uma forma de equilibrar o uso do processador de forma mais eficiente e justa. Lembrando que no escalonamento preemptivo o processador pode parar um processo quando finda seu tempo (timeslice) - a figura da questão anterior ilustra o caso (interrupção por tempo).

Comandos relacionados: top.

  • É possível haver vários sistemas de arquivos montados ao mesmo tempo, de forma hierárquica? Por quê?

Nada impede que haja vários sistemas de arquivos montados, desde que eles estejam em formato compreensível pelo kernel - para depois serem abstraídos às aplicações (veja o Virtual File System do Linux). Como o sistema de arquivos corrente, global, já é hierárquico (árvore de diretórios), a montagem sequencial dos vários sistemas se dará de forma natural.

Comandos relacionados: mount, umount.

  • O que é swap e qual a sua função?

Swap é um tipo de partição da memória secundária, formatada para servir como extensão da memória principal. Juntas, elas formam a memória virtual - que será utilizada pelas aplicações. Para manipular as partições de troca, ou swap, veja o Guia Foca Linux. Cabe destacar que este tipo de memória é mais barata e lenta.

Comandos relacionados: free.

  • O que é um interpretador de comandos? Cite um exemplo e um cenário onde ele apareça.

O interpretador de comandos, ou shell, é uma das aplicações voltadas para o usuário. Esta, em particular, é um terminal em formato texto para interpretador ordens do usuários: os comandos. Há vários interpretadores, onde cada um deles implementa uma linguagem para facilitar a entrada de dados ao computador. O bash é talvez o interpretador mais utilizado nos Linux "de usuário": Ubuntu, Mandriva, etc.

Comandos relacionados: bash.

  • O que é kernel? Por que ele é tão importante? Cite pelo menos 3 funções dele.

O kernel é nada mais que o núcleo do sistema operacional, sendo responsável pela maioria das funções ligadas ao S.O. Dentre as suas funções, podemos destacar: gerência de memória, gerência de processos, controle do sistema de arquivos (E/S).

  • Qual a diferença entre arquivo executável e processo? Processos consomem recursos como processamento, memória e barramento: comente e mostre em uma captura de tela.

O arquivo executável é um arquivo regular, cujo conteúdo é um programa - em código de máquina ou passível de interpretação por outro programa (Python, PHP, etc.). Já o processo é um programa carregado em memória, consumindo recursos da máquina (processamento, memória, E/S) para realizar as suas instruções. Portanto, um arquivo executável será carregado em memória para virar um processo (cuidado com código interpretado: o processo será, na verdade, o seu interpretador, e associado a ele o código a interpretar).

Comandos relacionados: ps, top, vmstat.

31/08: Preparativos para a prova

  • Atividades: o que este faz este script? Consegue "execuá-lo" mentalmente?
# Etapa 1 - Entrar no diretório "/home/aluno":
cd /
cd home
cd aluno
echo "Estou no diretório /home/aluno."
sleep 1
# Etapa 2 - Criar o diretório "q2":
mkdir q2
echo "Criei o subdiretório q2."
sleep 1
# Etapa 3 - Criar os subdiretórios "a", "b", "c" e "d" sob "q2":
cd q2	
mkdir a
mkdir b
mkdir c
mkdir d
echo "Criei os subdiretório a, b, c, e d."
# Etapa 4 - Criar o arquivo "h" sob "q2" com o conteúdo "xyz":
touch h
echo "xyz" > h
# Etapa 5 - Criar os diretórios "e" e "f" sob "a":
cd a
mkdir e
mkdir f
# Etapa 6 - Criar o arquivo "g" sob "d" com o conteúdo "123":
cd ../
cd d
echo "123" > g
  • Dada a seguinte estrutura de diretórios e arquivos:

<graphviz> digraph Dirs { antialias = true splines = true

e [shape=record] i [shape=record] k [shape=record] f [shape=record,label="<0>f|<1>123456"] j [shape=record,label="<0>j|<1>654321"]

a -> b a -> c a -> d a -> e e -> i [label="link simbólico",arrowhead=onormal] a -> f d -> g g -> h h -> i i -> f [label="link simbólico",arrowhead=onormal] c -> h [label="link simbólico",arrowhead=onormal] g -> j a -> k k -> j [label="link simbólico",arrowhead=onormal] b -> l l -> d [label="link simbólico",arrowhead=onormal] } </graphviz>

Nomenclatura:

  1. Círculo ou elipse: diretório
  2. Quadrado ou retângulo: arquivo
  3. Flecha com ponta escura: hard link
  4. Flecha com ponta vazada (clara): soft link

Perguntas referentes ao grafo anterior:

  1. Os arquivos "e" e "i" contêm o mesmo conteúdo?
  2. Os diretórios b e d contêm o mesmo conteúdo?
  3. Como criar esta estrutura de diretórios e arquivos? Apresente a sequência de passos ou operações; em seguida, a sequência de comandos de shell (interpretador de comandos).
  • Perguntas que "podem" cair na prova:
    • No artigo do UNIX, fala-se no diretório "..". O que ele significa?
    • A linguagem de programação C ajudou na disseminação do sistema UNIX. Comente.
    • Qual a relação entre usuário, uid e /gid?
    • Qualquer usuário pode listar os processos do sistema usando o comando ps? Por quê? Em que parte do sistema esta permissão está dita? Como garantir que apenas o usuário joao possa fazê-lo? Assuma que o usuário joao já exista no sistema; além disso, ignore o caso do usuário root, que ignora tais permissões.
    • Salve o seu histórico de comandos em um arquivo texto.
    • Apresente, na tela, apenas os nomes dos usuários cadastrados no sistema EXCETO o root. Dicas: veja o arquivo /etc/passwd; além disso, os comandos grep e cut, que podem auxiliar na tarefa.
    • Um estado bloqueado pode voltar a ser executado? O que o impede e como liberá-lo?
    • Um pid está, de alguma forma, associado a um uid. Comente.
    • Um arquivo texto pode ser interpretado? Como?
    • O que é E/S? Exemplifique-o com um comando.

02/09: Prova

1a. Aula: "Cola"

  • Usuários
    • Cadastro de usuários e grupos
    • Grupos primário e secundários
    • uid e gid
  • Processos
    • pid
    • Estados
  • Sistemas de arquivos
    • Diretórios
      • Diretórios importantes
      • Comandos de navegação
    • Arquivos
      • Criação e modificação
      • Permissões e propriedades
      • Comandos de manipulação
    • Links simbólicos
      • Criação de links

2a. e 3a. Aulas: Prova teórica

Lógica de Programação

09/09: Introdução

  • Problema 1: quais os arquivos, do sistema de arquivos corrente, com tamanho zero?
  1. Sequência de passos:
    1. Listar todos os arquivos.
    2. Verificar o tamanho de cada arquivo acima listado.
    3. Selecionar apenas os arquivos com tamanho zero.
  2. Proposta de solução em shell script - em 3 etapas conforme a sequência acima descrita:
# find / | xargs du | grep ^0

<graphviz> digraph Passos { rankdir=LR find [shape=record,label="<0>1º: Listar todos|<1>find /"] xargs [shape=record,label="<0>2º: Verificar o tamanho de cada|<1>xargs du"] grep [shape=record,label="<0>3º: Selecionar baseado no padrão|<1>grep ^0"] find:1 -> xargs:1 -> grep:1 [label="|"] } </graphviz>

  • Problema 2: reconhecimento de firma. Proposta de solução (em formato textual):
  1. Distribuir senhas sequencialmente.
  2. Atendimento por ordem sequencial das senhas. Se for preferencial, anteder; caso contrário, atender o próximo número.
  3. Recebimento dos documentos.
  4. Verifica se o assinante está presente. Se estiver, tudo bem; caso contrário, verifica se o documento pode ser reconhecido por semelhança.
  5. Verifica a existência de uma ficha relacionada ao nome do documento.
  6. Se existir,pegar a ficha; caso contrário, e se a pessoa estiver presente, cria uma ficha.
  7. Assinatura de um termo de comparecimento com data.
  8. Comparação das assinaturas dos documentos e do termo.
  9. Se forem iguais, reconhece, aplica o selo e leva ao tabelião assinar; caso contrário negar o reconhecimento.
  10. Recebimento do pagamento.
  11. Devolução dos documentos.
  12. Armazenamento da ficha - de volta ao fichário.
  13. Fim do reconhecimento.
  • Material de apoio: Lógica de Programação, de Paulo Sérgio de Moraes, páginas 4 a 7 - consulte as referências bibliográficas da disciplina.

14/09: Desenvolvendo algoritmos

  • Problema 1: desenhar um triângulo equilátero, um isósceles e outro escaleno.
  • Problema 2: desenhar um pentagrama (estrela de cinco pontas)
  1. Leitura do enunciado
  2. Abstração do problema: o pentagrama é um processo repetitivo (5x):
    1. Desenhar uma linha representando um lado da figura
    2. Definir o ângulo interno para traçar a próxima linha
  3. Definição da sequência de passos:
    1. Desenhar uma linha de tamanho X
    2. Alterar para 144 graus para um lado, no caso direito
    3. Desenhar uma linha de tamanho X
    4. Alterar para 144 graus para um lado, no caso direito
    5. Desenhar uma linha de tamanho X
    6. Alterar para 144 graus para um lado, no caso direito
    7. Desenhar uma linha de tamanho X
  4. A proposta de solução utiliza a linguagem Logo para apresentação do resultado. Foi utilizado o programa Kturtle para esboçar a figura final.
reset
turnleft 54
forward 100
turnright 144
forward 100
turnright 144
forward 100
turnright 144
forward 100
turnright 144
forward 100
  • Material de apoio: páginas 8 a 11 da apostila de Lógica de Programação.

16/09: Pseudocódigo e diagrama de blocos

  • Tópicos: pseudocódigo, regras de construção, etapas de um programa, diagramas de bloco.
  • Problema 1: o clássico problema da troca de uma lâmpada.
  1. Abstração do problema: considere a lâmpada do tipo incandescente instalada no teto da sala.
  2. Pseudocódigo: ações imperativas em linguagem natural (Português).
    1. Pegue uma escada.
    2. Abra a escada embaixo da lâmpada velha.
    3. Suba na escada.
    4. Retire a lâmpada velha.
    5. Desça da escada.
    6. Descarte a lâmpada velha.
    7. Pegue uma lâmpada nova.
    8. Suba na escada.
    9. Inslate a lâmpada nova no bocal.
    10. Desça da escada.
    11. Feche a escada.
    12. Guarde a escada.

O diagrama de bloco do pseudocódigo anterior será bastante simples: uma sequência única de passos a serem executados. <graphviz> digraph Lampada { Início "Pegue uma escada." [shape=record] "Abra a escada embaixo da lâmpada velha." [shape=record] "Suba na escada." [shape=record] "Retire a lâmpada velha." [shape=record] "Desça da escada." [shape=record] "Descarte a lâmpada velha." [shape=record] "Pegue uma lâmpada nova." [shape=record] "Suba novamente na escada." [shape=record] "Instale a lâmpada nova no bocal." [shape=record] "Desça novamente da escada." [shape=record] "Feche a escada." [shape=record] "Guarde a escada." [shape=record] Fim

Início -> "Pegue uma escada."-> "Abra a escada embaixo da lâmpada velha." -> "Suba na escada." -> "Retire a lâmpada velha." -> "Desça da escada." -> "Descarte a lâmpada velha." -> "Pegue uma lâmpada nova." -> "Suba novamente na escada." -> "Instale a lâmpada nova no bocal." -> "Desça novamente da escada." -> "Feche a escada." -> "Guarde a escada." -> Fim } </graphviz>

Contudo, nem mesmo o processo de trocar uma lâmpada é tão linear. Em um cenário mais próximo da vida real, haverá dúvidas e decisões a serem tomadas durante o processo: <graphviz> digraph LampadaComDecisao { Início "Há uma escada disponível?" [shape=diamond] "Pegue uma escada." [shape=record] "Abra a escada embaixo da lâmpada velha." [shape=record] "Suba na escada." [shape=record] "Retire a lâmpada velha." [shape=record] "Desça da escada." [shape=record] "Descarte a lâmpada velha." [shape=record] "Há uma lâmpada nova?" [shape=diamond] "Compre uma." [shape=record] "Pegue uma lâmpada nova." [shape=record] "Suba novamente na escada." [shape=record] "Instale a lâmpada nova no bocal." [shape=record] "Desça novamente da escada." [shape=record] "Feche a escada." [shape=record] "Guarde a escada." [shape=record] Fim

Início -> "Há uma escada disponível?"

 "Há uma escada disponível?" -> "Pegue uma escada." [label=Sim]
 "Há uma escada disponível?" -> Fim [label=Não]

"Pegue uma escada."-> "Abra a escada embaixo da lâmpada velha." -> "Suba na escada." -> "Retire a lâmpada velha." -> "Desça da escada." -> "Descarte a lâmpada velha." -> "Há uma lâmpada nova?"

 "Há uma lâmpada nova?" -> "Pegue uma lâmpada nova." [label=Sim]
 "Há uma lâmpada nova?" -> "Compre uma." [label=Não]

"Compre uma." -> "Há uma lâmpada nova?" "Pegue uma lâmpada nova." -> "Suba novamente na escada." -> "Instale a lâmpada nova no bocal." -> "Desça novamente da escada." -> "Feche a escada." -> "Guarde a escada." -> Fim } </graphviz>


21/09: Atividades com pseudocódigo e diagrama de blocos

  • Realize as atividades oferecidas na apostila: páginas 8 a 18.
  • Atividades complementares - responda em pseudocódigo e em diagrama de blocos (veja a nomenclatura na apostila):
    1. Leia 3 números inteiros. Apresente-os de volta nas ordens crescente e decrescente.
    2. Leia dois números inteiros, aplique as 4 funções básicas (soma, subtração, multiplicação e divisão) e apresente os resultados.
    3. Leia um número e informe se é divisível por 3.
    4. Dada uma equação de 2º grau, leia os três números e aplique a fórmula de Bhaskara - para obter a(s) resposta(s).
    5. Baseado nas atividades do programa Kturtle - utilizado no desenho do pentagrama - leia dois números: a quantidade de lados (entre 5 e 8) e o comprimento do segmento de linha para, em seguida, desenhar a estrela.

Obs.: este trabalho servirá como complemento de conceito na próxima avaliação.

23/09: Constantes e variáveis

  • Tópicos: constantes, variáveis. Exemplo: desenho do pentagrama.
  • Realize as atividades oferecidas na apostila: páginas 19 a 25.
  • Problema 1: conversão de moedas.
  • Problema 2: conforme a tabela progressiva para o cálculo mensal do IRPF, construa um programa que calcula o imposto retido na fonte. Apresente pseudocódigo e diagrama de blocos.
    • Informe, em seguida, quantos dias por ano o funcionário trabalha para pagar apenas os impostos. Considere como parte do problema anos bissextos (dica: 2000 foi um ano bissexto...).
  • Problema 3: Construa um programa que informa se um número inteiro é divisível por 2 e/ou por 3. Apresente pseudocódigo e diagrama de blocos.
  • Problema 4: apresente o diagrama de blocos para o problema do reconhecimento de firma, do dia 09/09. Atenção: a solução deste problema é, provavelmente, mais refinada que aquela apresentada no dia...

28/09: Operadores

  • Tópicos: operadores lógicos e aritméticos, expressões.

30/09: Estruturas

  • Tópicos: estruturas de decisão e repetição.

Problema 1

  • Enunciado: calcular a potência de 3 para um dado número inteiro.

Proposta de Solução 1

  • Pseudocódigo:
    • Entrada de dados
      • Leia o número.
    • Processamento
      • Multiplique o número por ele mesmo e armazene o resultado em PotênciaDe2.
      • Multiplique o número por PotênciaDe2 e armazene o resultado em PotênciaDe3.
    • Saída de dados
      • Escreva o número armazenado em PotênciaDe3.
  • Codificação em shell script:
#!/bin/bash

# ENTRADA DE DADOS
# Leia o número.
echo "Digite um número inteiro:"
read numero

# PROCESSAMENTO
# Multiplique o número por ele mesmo e armazene o resultado em PotênciaDe2.
potenciaDe2=`expr $numero \* $numero`
#
# Multiplique o número por PotênciaDe2 e armazene o resultado em PotênciaDe3.
potenciaDe3=`expr $numero \* $potenciaDe2`

# SAÍDA DE DADOS
# Escreva o número armazenado em PotênciaDe3.
echo "O resultado é:"
echo $potenciaDe3

Proposta de Solução 2

  • Pseudocódigo:
    • Entrada de dados
      • Leia o número.
      • Armazene o número na variável Resultado.
    • Processamento
      • Armazene o número 1 na variável Potência.
      • Enquanto Potência < 3 faça
        • Multiplique o número e Resultado.
        • Adicione 1 a Potência
      • FimEnquanto
    • Saída de dados
      • Escreva o número armazenado em Resultado.
  • Codificação em shell script:
#!/bin/bash

# ENTRADA DE DADOS
#Leia o número.
echo "Digite um número inteiro"
read numero
#
#Armazene o número na variável Resultado.
resultado=$numero

# PROCESSAMENTO
#Armazene o número 1 na variável Potência.
potencia=1
#
#Enquanto Potência < 3 faça
#    Multiplique o número e Resultado.
#    Adicione 1 a Potência
#FimEnquanto
while [ $potencia -lt '3' ]; do
  resultado=`expr $numero \* $resultado`
  potencia=`expr $potencia + 1`
done

# SAíDA DE DADOS
#Escreva o número armazenado em Resultado.
echo "O resultado é:"
echo $resultado

Proposta de Solução 3

  • Pseudocódigo:
    • Entrada de dados
      • Leia o número.
      • Armazene o número na variável Potência.
    • Processamento
      • Para "2 3" faça
        • Multiplique número e Potência.
      • FimPara
    • Saída de dados
      • Escreva o número da variável Potência.
  • Codificação em shell script:
#!/bin/bash

# ENTRADA
#Leia o número.
echo "Digite um número inteiro:"
read numero
#Armazene o número na variável Potência.
potencia=$numero

# PROCESSAMENTO
#Para "2 3" faça
#  Multiplique número e Potência.
#FimPara
for lista in 2 3; do
  echo "Multiplicando pela $lista vez..."
  potencia=`expr $numero \* $potencia`
done

# SAíDA
#Escreva o número da variável Potência.
echo "A potência de 3 de $numero é:"
echo $potencia

Problema 2

  • Enunciado: ler números indefinidamente (da entrada de dados) até encontrar um número par.

Proposta de Solução 1

  • Pseudocódigo:
    • Entrada de dados
      • Leia número.
    • Processamento
      • Enquanto "número não for par" faça
        • leia número.
      • FimEnquanto
    • Saída de dados
      • Mostre número par.

Problema 3

  • Enunciado: identificar se um dado número inteiro é potência de outro. Ex.: ler o número 225 (entrada) e verificar que o mesmo é potência do número 15 (saída).

Problema 4

  • Enunciado: identificar se um número é primo.

Problema 5

  • Enunciado: identificar se um número é perfeito.

Problema 6

  • Enunciado: construa a tabuada - de 1 a 9.

05/10: Atividades pré-prova

  • Tópicos: revisão dos conceitos vistos em aula, exercícios.

Problema 1

  • Enunciado: Leia 5 números e ordene-os em ordem crescente e decrescente.

Problema 2

  • Enunciado: Apresente a sequência de Fibonacci até o número 1000.

Problema 3

  • Enunciado: Escolha um número aleatoriamente. Peça ao usuário para descobri-lo e só pare quando o número for encontrado.

Problema 4

  • Enunciado: Apresente as potências de um número cujo resultado é par e inferior a 1000. Informe o graus de potência e o resultado em cada operação.

07/10: Prova de lógica

12/10: Feriado

Linguagem de Programação C

14/10: Ambiente de desenvolvimento

  • Tópicos: editor de código, compilador, depurador, primeiros programas.

<graphviz> digraph Etapas { "Código-fonte em C" [shape=circle] "Código Assembly" "Código de máquina" [shape=Mrecord] "Código executável" [shape=record]

"Código-fonte em C" -> "Código Assembly" [label="Compilação (compiling)"] "Código Assembly" -> "Código de máquina" [label="Montagem (assembling)"] "Código de máquina" -> "Código executável" [label="Vinculação (linking)"] } </graphviz>

  • Exemplo de código em C:
#include <stdio.h>

int main(){

  // Declaração de variáveis
  int x;
  int y;
  int soma;

  // Entrada de dados
  scanf("%d",&x);
  scanf("%d",&y);

  // Processamento
  soma = x + y;

  // Saída de dados
  printf("x = %d\n",x);
  printf("y = %d\n",y);
  printf("x + y = %d\n",soma);
}
  • Exercício de programação em C: para resolver uma equação de 2º grau, leia os três números e aplique a fórmula de Bhaskara - para obter a(s) resposta(s).

19/10: Projeto final

Até o dia 11/12 será desenvolvido o projeto final da disciplina utilizando a linguagem C. O projeto está dividido em 4 etapas, a serem entregues conforme calendário estipulado no projeto.

Início da Etapa 1

Para realizar a primeira etapa, será necessário adquirir os conhecimentos básicos da linguagem, em especial tipos e variáveis. Para tanto, foi proposto em sala, hoje, um exercício envolvendo tipos, variáveis, estruturas de decisão e de repetição.

  • Proposta: apresente a sequência de Fibonacci até um valor limite estipulado pelo usuário. Além disso, para cada número da sequência adicione a letra p caso o mesmo seja primo.
  • Primeira parte: sequência de Fibonacci com número de parada. Arquivo fibonacci.c:
#include <stdio.h>

int main()
{
	int a;
	int b;
	int b_original;
	int limite;
	
	a = 0;
	b = 1;
	
	printf("Informe o valor limite para a sequência de Fibonacci (número inteiro): ");
	scanf("%d", &limite);
	
	printf("A sequência de Fibonacci é:");
	printf(" %d", a);
	while ( b <= limite )
	{
		printf(" %d",b);
		b_original = b; // Como Fibonacci utiliza *a* e *b* para somar o
		b = a + b;      // próximo, e *b* tem o seu valor modificado, é
		a = b_original; // preciso guardar o seu "original" para *a*.
	}
	printf(".\n");
}

Compilando e, em seguida, executando o programa:

$ gcc fibonacci.c -o Fibonacci
$ ./Fibonacci
Informe o valor limite para a sequência de Fibonacci (número inteiro): 144
A sequência de Fibonacci é: 0 1 1 2 3 5 8 13 21 34 55 89 144.
  • Segunda parte: teste de primalidade para cada número da sequência. É interessante, em casos assim, criar uma nova função, a ser usada devidamente na função principal (main). Essa função retornará um valor lógico: 0 para não e 1 para sim.
int primo(int numero)
{
	int divisor;
	int resto;
	int ehPrimo = 1;
	
	for(divisor = numero - 1; divisor > 1; divisor--)
	{
		resto = numero % divisor;
		if(resto == 0)
		{
			ehPrimo = 0;
		}
	}
	return ehPrimo;
}

Ao final, o programa será modificado para chamar a função que testa a primalidade de cada um dos números:

#include <stdio.h>

int primo(int numero)
{
	int divisor;
	int resto;
	int ehPrimo = 1;
	
	for(divisor = numero - 1; divisor > 1; divisor--)
	{
		resto = numero % divisor;
		if(resto == 0)
		{
			ehPrimo = 0;
		}
	}
	return ehPrimo;
}

int main()
{
	int a;
	int b;
	int b_original;
	int limite;
	
	a = 0;
	b = 1;
	
	printf("Informe o valor limite para a sequência de Fibonacci (número inteiro): ");
	scanf("%d", &limite);
	
	printf("A sequência de Fibonacci é:");
	printf(" %dp", a);
	while ( b <= limite )
	{
		if(primo(b) == 1)
		{
			printf(" %dp",b);
		} else
		{
			printf(" %d",b);
		}
		b_original = b; // Como Fibonacci utiliza *a* e *b* para somar o
		b = a + b;      // próximo, e *b* tem o seu valor modificado, é
		a = b_original; // preciso guardar o seu "original" para *a*.
	}
	printf(".\n");
}

Compilando pela segunda vez, chegamos ao resultado final do exercício:

$ gcc fibonacci.c -o Fibonacci
$ ./Fibonacci
Informe o valor limite para a sequência de Fibonacci (número inteiro): 144
A sequência de Fibonacci é: 0p 1p 1p 2p 3p 5p 8 13p 21 34 55 89p 144.

21/10: Estrutura de um Programa em C e Números

Atividades: construa um programa que apresenta na tela...

  • Os números inteiros em sequência ascendente:
#include <stdio.h>

int main()
{
	int inteiro;
	
	inteiro = 0;

	printf("Números inteiros em ordem ascendente:");
	while ( 1 == 1 ) // Condição sempre satisfeita
	{
		printf(" %d",inteiro);
		inteiro = inteiro + 1;
	}
}
  • Os números inteiros pares em sequência ascendente: modifica-se apenas a operação na linha 13.
#include <stdio.h>

int main()
{
	int inteiro;
	
	inteiro = 0;

	printf("Números inteiros pares em ordem ascendente:");
	while ( 1 == 1 ) // Condição sempre satisfeita
	{
		printf(" %d",inteiro);
		inteiro = inteiro + 2;
	}
}
  • Alternativamente, pode-se utilizar uma função dedicada para somar 2 a cada número, criando a sequência:
#include <stdio.h>

int adicioneDois(int numero)
{
	numero = numero + 2;
	return numero;
}

int main()
{
	int inteiro;
	
	inteiro = 0;

	printf("Números inteiros pares em ordem ascendente:");
	while ( 1 == 1 ) // Condição sempre satisfeita
	{
		printf(" %d",inteiro);
		inteiro = adicioneDois(inteiro);
	}
}

É a função das linhas 3 a 7 que, em tempo de execução, é chamada na linha 19. O mesmo raciocínio também se aplica ao exercício anterior (para somar 1).

  • Os números inteiros ímpares em sequência ascendente: a diferença entre pares e ímpares está apenas no primeiro número. Portanto, modifica-se apenas a linha 13:
#include <stdio.h>

int adicioneDois(int numero)
{
	numero = numero + 2;
	return numero;
}

int main()
{
	int inteiro;
	
	inteiro = 1;

	printf("Números inteiros ímpares em ordem ascendente:");
	while ( 1 == 1 ) // Condição sempre satisfeita
	{
		printf(" %d",inteiro);
		inteiro = adicioneDois(inteiro);
	}
}
  • Os números inteiros pares ou ímpares: o usuário escolhe. Neste exemplo, é utilizado um caracter como item de escolha:
    • Linha 12: declaração da nova variável do tipo caracter.
    • Linhas 14 e 15: leitura da variável pela entrada de dados do usuário.
    • Linhas 16 a 31: decisão entre P (16), I (22) ou o encerramento do programa pela digitação de um valor inválido (28-29).
#include <stdio.h>

int adicioneDois(int numero)
{
	numero = numero + 2;
	return numero;
}

int main()
{
	int inteiro;
	char escolha;
	
	printf("Digite P para números pares ou I para números ímpares: ");
	scanf("%c", &escolha);
	if ( escolha == 'P' )
	{
		inteiro = 0;
	}
	else
	{
		if ( escolha == 'I' )
		{
			inteiro = 1;
		}
		else
		{
			printf("Escolha errada!\n");
			return;
		}
	}

	printf("Números inteiros em ordem ascendente:");
	while ( 1 == 1 ) // Condição sempre satisfeita
	{
		printf(" %d",inteiro);
		inteiro = adicioneDois(inteiro);
	}
}
  • Números pares ou ímpares, com valor limite: há um número limite para os valores apresentados na tela.
Coloque aqui o código-fonte em C.
  • Números pares ou ímpares acima de 1 bilhão com valor limite: os números, ao invés de 0 (zero), iniciarão em 1.000.000.000 (1 bilhão). Mantém-se a regra do número limite.

Recuperação da Prova

A recuperação da prova de lógica consiste em um trabalho a ser entregue dia 28/10, quarta-feira, durante a aula.

O problema é o seguinte: construa uma calculadora baseada notação polonesa inversa. Para facilitar o desenvolvimento da aplicação, considere que é possível armazenar até 3 valores na pilha de números, além de utilizar apenas as quatro operações matemáticas: +, -, * e /. A resposta deve ser dada quando houver apenas um número na pilha.

O conceito de pilha é interessante: os primeiros valores adicionados serão os últimos escolhidos, como uma pilha de objetos: <graphviz> digraph Pilha {

subgraph clusterOP { Adiciona Retira } subgraph clusterP { label=Pilha 4 3 2 }

Adiciona -> 4 4 -> Retira 4 -> 3 -> 2 2 -> 3 -> 4 } </graphviz> No exemplo ao lado: o número 2 foi o primeiro adicionado e, portanto, o último a ser escolhido.

Exemplos de operações utilizando notação polonesa inversa:

2 3 +
= 5
2 3 * 4 +
= 10
7 6 3 + -
= -2
2 3 4 * +
= 14

Os números são armazenados para uso posterior. Para cada operação matemática, são "escolhidos" os últimos números inseridos, aplicada a operação e o resultado retorna à pilha de números. Portanto, a ordem dos elementos faz diferença na execução.

Neste último exemplo, seguem os passos da solução:

  1. A pilha está com os valores 2 (base) 3 4 (topo).
  2. Aparece a operação de multiplicação. São utilizados os valores mais próximos do topo: 3 e 4. O resultado é 12.
  3. Os valores 3 e 4 são retirados da pilha e retorna apenas o valor 12. A pilha fica assim: 2 (base) 12 (topo).
  4. Aparece a operação de soma. São utilizados os valores mais próximos do topo: 2 e 12. O resultado é 14.
  5. Os valores 2 e 12 são retirados da pilha e retorna apenas o valor 14.
  6. Como há apenas um número na pilha, o resultado final é apresentado: 14

Outro exemplo:

8 8 6 4 3 + - + *
= 56

A sequência das operações, com a pilha resultante, é:

4 3 +
  Pilha = 8 8 6 7 - + *
6 7 -
  Pilha = 8 8 -1 + *
8 -1 +
  Pilha = 8 7 *
8 7 *
  Pilha = 56
= 56 

Dica: o programa dc utiliza este tipo de notação. A calculadoraHP12C, muito utilizada para Contabilidade e Economia, também se baseia nos mesmos princípios.

O trabalho deve ser entregue em papel ou mídia digital, durante a aula, contendo pseudocódigo e diagrama de blocos da solução do problema. Lembre-se: até 3 números armazenados e as quatro operações básicas.

26/10: Etapa 1 - Tipos e Funções

  • Referência externa: aulas 2, 3, 4, 5 e 11 do curso de C.
  • Tipos simples e compostos
#include <stdio.h>

int main()
{
	char nome[50];
	int idade;
	
	printf("Digite o nome da pessoa: ");
	fgets(nome,sizeof(nome),stdin);
	printf("Digite a idade da pessoa: ");
	scanf("%d", &idade);

	printf("%s tem %d anos.", nome, idade);
}
#include <stdio.h>

int main()
{
	struct pessoa
	{
		char nome[50];
		int idade;
	};
	struct pessoa Aluno;
	
	printf("Digite o nome da pessoa: ");
	fgets(Aluno.nome,sizeof(Aluno.nome),stdin);
	printf("Digite a idade da pessoa: ");
	scanf("%d", &Aluno.idade);

	printf("%s tem %d anos.", Aluno.nome, Aluno.idade);
}

28/10: Etapa 1 - Pseudocódigo

Pseudocódigo da primeira etapa:

  1. Enquanto opção for diferente de "Sair do programa" faça
    1. Apresente as opções para o usuário
      1. Primeira opção: criar um evento
      2. Segunda opção: mostrar um evento já cadastrado
      3. Terceira opção: Sair do programa
    2. Leia a opção do usuário
    3. Se a opção é de criação de um evento então
      1. Leia título
      2. Leia horários de início e fim
      3. Leia data e horário de criação
      4. Leia descrição
      5. Leia local de realização
      6. Leia outros participantes.
      7. Leia estado.
      8. Leia categoria: pessoal, trabalho, etc.
      9. Leia recorrência: com que regularidade ocorre e prazo.
    4. Fim Se
    5. Se a opção é mostrar um evento então
      1. Se já existe um evento cadastrado
        1. Apresente o evento, um item por linha
      2. Fim Se
    6. Sim Se
  2. Fim Enquanto
  • Para pensar: o que faz este código:
#include <stdio.h>
#include <string.h>

int main()
{
	int contador = 0;
	int tamanhoDaString;

	struct pessoa
	{
		char nome[50];
	};
	struct pessoa Aluno;
	
	printf("Inicie a contagem com: ");
	scanf("%d",&contador);
	while(contador < 50)
	{
		printf("Digite o nome da pessoa: ");
		fgets(Aluno.nome,50,stdin);
		if(Aluno.nome[0] == '\n')
		{
			fgets(Aluno.nome,50,stdin);
		}
		tamanhoDaString = strlen(Aluno.nome);
		if( Aluno.nome[tamanhoDaString-1] == '\n' )
			Aluno.nome[tamanhoDaString-1] = 0;

		printf("Nome = %s\n",Aluno.nome);
		contador++;
	}
}

02/11: Etapa 2 - O uso de funções para organizar código

O interessante, quando o programa adquire complexidade, é organizar o código em funções.

Leitura de uma linha inteira a partir da entrada de dados do usuário:

#include <stdio.h>
#include <string.h>


// Estruturas de Dados
struct pessoa
{
	char nome[50];
	int idade;
};


// Funções de entrada de dados
int lerNumero()
{
	int numero;
	int leuNumero = 1;
	do
	{
		if (leuNumero == 0)
		{
			printf("Ops! Problemas ao ler o número. Digite-o novamente: ");
		}
		leuNumero = scanf("%d", &numero);
		if (leuNumero == 0)
		{
			leuNumero = scanf("%*[^\n]");
		}
	} while (leuNumero == 0);
	return numero;
}

void lerLinha(char * linha) {
  scanf(" %[^\n]", linha);
}

struct pessoa lerValores()
{
	int tamanhoDaString;
	struct pessoa qualquerPessoa;
	printf("Nome da pessoa: ");
	// Como a leitura de string é diferente das outras variáveis,
	// isso é feito dentro da própria função.
	fgets(qualquerPessoa.nome,50,stdin);
	if(qualquerPessoa.nome[0] == '\n')
	{
		fgets(qualquerPessoa.nome,50,stdin);
	}
	tamanhoDaString = strlen(qualquerPessoa.nome);
	if(qualquerPessoa.nome[tamanhoDaString-1] == '\n')
	{
		qualquerPessoa.nome[tamanhoDaString-1] = 0;
	}
	printf("Idade da pessoa: ");
	qualquerPessoa.idade = lerNumero();
	return qualquerPessoa;
}

int mostrarValores(struct pessoa qualquerPessoa)
{
	printf("Nome da pessoa: %s\n", qualquerPessoa.nome);
	printf("Idade da pessoa: %d\n", qualquerPessoa.idade);
}


// Função principal: o programa
int main()
{
	int opcao = 0;
	int valoresJaLidos = 0;
	struct pessoa umaPessoa;
	do
	{
		printf("\nOpção 1 - Ler valores do usuário.\n");
		printf("Opção 2 - Mostrar valores já lidos do usuário.\n");
		printf("Opção 3 - Sair do programa.\n");
		printf("Digite a sua opção: ");
		opcao = lerNumero();
		switch(opcao)
		{
			case 1:
				umaPessoa = lerValores();
				valoresJaLidos = 1;
				break;
			case 2:
				if (valoresJaLidos == 1)
				{
					mostrarValores(umaPessoa);
				}
				else
				{
					printf("Ainda não há valores lidos do usuário. ");
					printf("Escolha a opção 1 para lê-los.\n");
				}
				break;
			case 3:
				printf("Fim do programa. Adeus!\n");
				break;
			default:
				printf("Opção inválida! Escolha um número entre 1 e 3.\n");
				break;
		}
	} while (opcao != 3);
}

04/11: Etapa 2 - Continuação da etapa anterior

Estruturando a primeira etapa do programa:

//Inclusão de outros arquivos e bibliotecas
#include <stdio.h>
#include <time.h>

//Estruturas de dados
// Estrutura de um evento:
//	- Título
//	- Data de início e de fim
//	- Horário de início e de fim
//	- Data e horário de criação
//	- descrição
//	- local
//	- estado: confirmado ou não?
//	- categoria: pessoal, trabalho, etc.
//	- recorrência: regularidade.
struct evento
{
	char titulo[50];
	char dataDeInicio[10];
	char dataDeTermino[10];
	char horarioDeInicio[5];
	char horarioDeTermino[5];
	char dataDeCriacao[10];
	char horaDeCriacao[5];
	char descricao[100];
	char local[100];
	char estado[20];
	char categoria[10];
	char recorrencia[20];
};


//Funções auxiliares
char* agora()
{
	time_t hora;
	time(&hora);
	return(ctime(&hora));
}


//Função principal
int main()
{
	int opcao;
	struct evento meuPrimeiroEvento;
	do
	{
		printf("\nOpção 1: criar um evento.\n");
		printf("Opção 2: mostar um evento já criado.\n");
		printf("Opção 3: Sair do programa.\n");
		printf("Digite a sua opção: ");
		scanf("%d", &opcao);
		switch(opcao)
		{
			case 1:
				printf("Digite o título do evento: ");
				scanf("%s", meuPrimeiroEvento.titulo);
				break;
			case 2:
				printf("Apenas um evento cadastrado!\n");
				printf("Título: %s\n", meuPrimeiroEvento.titulo);
				printf("Hora de criação: %s", agora());
				break;
			case 3:
				printf("Fim do programa. Adeus!\n");
				break;
		}
	} while (opcao != 3);
}


Exemplo de uso de vetores para utilizar, em um mesmo programa, várias estruturas de dados:

#include <stdio.h>

struct ponto
{
	int x;
	int y;
	int z;
};

int main()
{
	int indice;
	struct ponto Vertices[3];
	
	for(indice=0; indice <=2; indice++)
	{
		printf("\nPonto %d\n", indice);
		printf("Informe x: ");
		scanf("%d", &Vertices[indice].x);
		printf("Informe y: ");
		scanf("%d", &Vertices[indice].y);
		printf("Informe z: ");
		scanf("%d", &Vertices[indice].z);
	}
	
	printf("\nResultados:\n");
	for(indice=0; indice<=2; indice++)
	{
		printf("Figura %d:", indice);
		printf(" %d", Vertices[indice].x);
		printf(" %d", Vertices[indice].y);
		printf(" %d\n", Vertices[indice].z);
	}
}

09/11: Etapa 2 - Desenvolvimento em etapas

Programação requer dedicação, disciplina e, antes de tudo, perseverança :-)

Um programa simples, como este:

void main()
{
}

é o início de praticamente qualquer programa em C, uma vez que contém a função principal, main(). Neste caso, definida com um conjunto vazio de instruções - limitado pelas chaves.

16/12: Recuperação

  • Tópicos: prova de recuperação.


Página principal da disciplina