Aula prática 2

Sumário

Objectivos

Os alunos no final desta aula deverão conhecer:

  1. A estrutura básica dos programas em C++.
  2. Os tipos básicos do C++.

Deverão também ser capazes de:

  1. Fazer o traçado das instruções de um programa recorrendo apenas a um lápis e papel.
  2. Escrever programas simples em C++ usando variáveis, constantes e ciclos simples.
  3. Usar o editor XEmacs.
  4. Usar o depurador para tarefas simples de remoção de erros.

Caso os alunos sintam que os objectivos não foram atingidos na totalidade deverão concluir/repetir os exercícios desta aula autonomamente e ou recorrer aos horários de dúvidas.

Resumo

O resumo da matéria abordada nesta aula prática pode ser consultado aqui.

Exercícios

1.  Abra no editor o programa ex1.C (disponível no directório ~/IP/Aula2), compile-o, execute-o e faça o seu traçado.  Para isso execute os seguintes passos:
  1. Prima o ícone do Painel do editor XEmacs (que tem um gnu).
  2. Abra o ficheiro ~/IP/Aula2/ex1.C (ou /home/annnnn/IP/Aula2/ex1.C sendo nnnnn o seu número).  Use o botão Open do XEmacs (ou prima <ctrl-x> < ctrl-f>, ou ainda o item File/Open do menu).  Escreva (complete) o nome que aparece na janela, ou no mini-buffer.
  3. Compile o ficheiro fonte primindo o botão Compile.
  4. Leia os resultados da compilação.  Deverá apresentar a mensagem:
    1. Compilation finished at ...
  5. Execute o programa num Terminal.
  6. Prima o botão Debug (ou pressione <ctrl-c> <d>).
  7. Digite o nome do ficheiro executável (programa em linguagem máquina), que é ~/IP/Aula2/ex1, ou seleccione-o na caixa de diálogo entretanto aberta.
  8. Aparece outra janela com várias mensagens terminando com a prompt (gdb).
  9. À frente da prompt (gdb) dê o comando
    1. b main
  10. Aparecerá a mensagem
    1. Breakpoint 1 at ...
  11. Execute o programa dando o comando
    1. r
  12. O ficheiro fonte aparece noutra janela (ou na mesma em que o escreveu, se estiver aberta) com a linha de código a executar em fundo azul e precedida pelo sinal =>.
  13. Avance dando o comando
    1. n
    (uma vez para cada linha de instruções) até ultrapassar a linha
      cin >> primeiro_numero >> segundo_numero;
  14. Ao executar esta linha o sinal => e o fundo azul da linha desaparecem: o programa está à espera que o utilizador insira dados.
  15. Garanta que o cursor está na janela do depurador.
  16. Introduza dois números inteiros à frente da mensagem
    1. Introduza dois números inteiros:
    tendo o cuidado de os escrever após o espaço que está a seguir aos dois pontos:
      Introduza dois números inteiros: 123 12
  17. Prima <enter>.  Verificará que o sinal => reapareceu noutra linha, o que significa que a instrução anterior foi executada.  Esta instrução leu para as variáveis primeiro_numero e segundo_numero os valores 123 e 12, respectivamente.
  18. Execute o programa linha a linha dando o comando
    1. n
  19. Use o botão p= para ver o valor das variáveis (posicionando o cursor no nome de uma variável antes de premir o botão).
  20. Execute de novo o programa desde o início utilizando o comando disp(lay) do seguinte modo:
    1. r
      disp primeiro_numero
      disp segundo_numero
      disp resultado
  21. Em cada interrupção do programa é mostrado o valor actual das variáveis.  Repare que antes de lhes ser atribuído um valor pela primeira vez estas variáveis contêm um valor qualquer, i.e., lixo, uma vez que não foram inicializadas durante a sua definição.

Sempre que sair do depurador a meio da execução do programa em depuração é normal que apareça o seguinte texto na janela de mensagens:

This program is running. Exit anyway? (y or n)

Mude o cursor para essa janela e escreva: prima y e <enter> para que o XEmacs termine a execução do depurador.


2.a)  Escreva um programa, ou altere o ex1.C (disponível no directório ~/IP/Aula2), de modo a que peça ao utilizador para inserir dois números inteiros e os some.  Depois de executar o programa, o seu ecrã (Consola de Comandos) deve ter o seguinte aspecto:

Insira dois números: 12 234
A soma dos dois números inseridos é: 246

Recomenda-se que antes de começar as alterações crie um novo ficheiro fazendo File/Save As ... e dê outro nome ao ficheiro (por exemplo ~/IP/Aula2/ex2a.C ) de modo a preservar a resposta ao exercício 1.  Lembre-se que é necessário compilar de novo o programa depois de fazer as alterações.

2.b)  Altere o seu programa de modo a que possa inserir números decimais (com vírgula).

2.c)  Altere o seu programa para fazer a leitura de cinco números e somá-los.  Para facilitar a edição do programa use o comando <ctrl-k> <ctrl-k>, que corta uma linha inteira de cada vez, e <ctrl-y>, que cola o que foi cortado anteriormente.  Experimente posicionar o cursor no início da linha

int primeiro_numero;

e prima <ctrl-k> duas vezes, i.e. sem largar a tecla ctrl pressione k duas vezes: desapareceram duas linhas.  Depois <ctrl-y> três vezes (apareceram de novo as duas linhas, repetidas três vezes).  Aprenda a usar estas combinações de teclas de modo a facilitar a alteração dos seus programas.

2.d)  Se usou mais do que duas variáveis para fazer a alínea anterior, reescreva o seu programa usando apenas duas variáveis.  Uma delas deve guardar o valor lido enquanto a outra deve guardar o valor acumulado da soma dos números inseridos até ao momento.  Faça o traçado deste programa (usando o depurador) visualizando a evolução de ambas as variáveis.

Atenção:  Repare que as variáveis de tipos básicos contêm inicialmente lixo se não foram inicializadas na sua definição.

2.e)  Use um ciclo for para repetir 5 vezes a leitura e a actualização da variável que guarda o valor acumulado (veja o resumo sobre as instruções de iteração).  Um ciclo que repete n vezes um conjunto de instruções tem a seguinte forma genérica:

for(int i = 0; i != n; ++i) {
    ... // instruções que serão repetidas n vezes.
}


3.  Escreva um programa que imprima no ecrã os caracteres do código ISO-Latin1 desde o caractere cujo código é 33 até ao caracter cujo código é 127 inclusive.  Lembre-se que ao atribuir um valor inteiro a uma variável do tipo char este é "transformado" no caractere correspondente do código em uso (neste caso ISO-Latin1).  Deve usar um ciclo que comece em 33 e termine em 127 inclusive.

Para imprimir no ecrã o código de um caracter (o int i) seguido do próprio caracter (o char c) use as seguintes instruções:

c = i;
cout << i << ": " << c << "  ";

Esta instrução mostra no ecrã o código do caracter (um número inteiro) , seguido de dois pontos e do caracter correspondente a esse código, e por fim escreve dois espaços para separar do código que se segue. Quando executar o programa devem aparecer no seu ecrã todos os caracteres ISO-Latin1 disponíveis com códigos entre 33 e 127:

33: !  34: "  35: #  36: $  37: %  38: &  39: '  40: (  41: )  42: *  43: +  44: ,  45: -  46: .  47: /  48: 0  49: 1  50: 2  51: 3  52: 4  53: 5  54: 6  55: 7  56: 8  57: 9  58: :  59: ;  60: <  61: =  62: >  63: ?  64: @  65: A  66: B  67: C  68: D  69: E  70: F  71: G  72: H  73: I  74: J  75: K  76: L  77: M  78: N  79: O  80: P  81: Q  82: R  83: S  84: T  85: U  86: V  87: W  88: X  89: Y  90: Z  91: [  92: \  93: ]  94: ^  95: _  96: `  97: a  98: b  99: c  100: d  101: e  102: f  103: g  104: h  105: i  106: j  107: k  108: l  109: m  110: n  111: o  112: p  113: q  114: r  115: s  116: t  117: u  118: v  119: w  120: x  121: y  122: z  123: {  124: |  125: }  126: ~  127: 

Os caracteres abaixo do número 33 são caracteres de controlo e a sua impressão no ecrã poderá dar origem a "coisas estranhas".


4.a)  Use o seu programa de soma (a versão feita para inteiros, na alínea 2.a)) para tentar somar os números 2147483647 e 2.  Faça um traçado do programa e tente perceber porque não funciona.

4.b)  Experimente "corrigir" o programa mencionado na alínea anterior usando variáveis do tipo float.  Funcionou?  Porquê?

Nota:  Para visualizar os float com precisão adequada, faça cout << setprecision(20); antes de os escrever (e faça #include <iomanip> no início do programa).

4.c)  Experimente "corrigir" o programa mencionado na alínea anterior usando variáveis do tipo double.  Funcionou?


5.  Abra o ficheiro ex5.C que se encontra no directório ~/IP/Aula2.

Tente compilar e executar este programa.  Funcionou?  Então corrija o programa de modo a que o valor de x seja de facto alterado e passe a conter o valor introduzido.


6.  Abra o ficheiro ex6.C que se encontra no directório ~/IP/Aula2):

Compile, execute e faça o traçado (com o depurador) deste programa examinando os valores que tomam as variáveis.  Acrescente novas linhas ao fim do programa, semelhantes às duas últimas linhas, de modo a testar o efeito de cada um dos operadores listados no resumo.

Repare que ao mostrar o valor de uma expressão booleana (e.g. cout << (x < y) << endl;)  o que aparece no ecrã é 0 caso a expressão seja falsa e 1 caso contrário.  A norma do C++ inclui o manipulador boolalpha para mostrar true e false em vez de 1 e 0 (e, se o ambiente estiver configurado para português, verdadeiro e falso).  O manipulador boolalpha pode utilizar-se da seguinte forma:

cout << boolalpha << (x < y) << endl;

Infelizmente a versão da biblioteca padrão do C++ que estamos a utilizar ainda não suporta este tipo de operação.

Atenção:  As precedências dos operadores são importantes.  Tente fazer:

cout << x < y << endl;

e depois

cout << (x < y) << endl;

e procure explicar porque uma destas instruções funciona e a outra não.


7.a)  Escreva um programa que, dado um caractere, escreva no ecrã o seu código.  Exemplo:

Por favor introduza um caractere: a
O código do caractere 'a' é: 97

7.b)  Escreva um programa que, dada uma letra minúscula, escreva no ecrã o seu número de ordem no alfabeto (assuma que se utiliza o código ISO-Latin1).  Exemplo:

Por favor introduza uma letra minúscula: a
A ordem da letra 'a' é: 1
Outro exemplo: Por favor introduza uma letra minúscula: c
A ordem da letra 'c' é: 3

7.c)  Escreva um programa que, dada uma letra, escreva no ecrã a letra seguinte no alfabeto (assuma de novo o código ISO-Latin1).


8.  Relembre a resolução do exercício 1. da Aula 1 (o máximo divisor comum, que se encontra no ficheiro ex1.C do directório ~/IP/Aula1).  Este algoritmo procede a uma busca exaustiva desde o menor dos dois números até encontrar o maior divisor comum a ambos.  Para números muito grandes, especialmente quando estes são mutuamente primos (i.e., o máximo divisor comum é 1), este método é muito pouco eficiente.  Sabendo que o máximo divisor comum tem as seguintes propriedades:

  1. mdc(m, n) = mdc(n ÷ m, m) se 0 < m e 0 <= n.
  2. mdc(0, n) = n, se 0 < n (e também mdc(0, m) = m, se 0 < m).
pense num algoritmo que possa efectuar o mesmo cálculo, i.e., descobrir qual o máximo divisor comum de dois números dados pelo utilizador, de um modo mais eficiente.  Um algoritmo é tanto mais eficiente quanto menor for o número de operações necessárias para atingir o resultado pretendido.

Antes de começar consulte a descrição da instrução if no resumo.

Implemente na forma de uma programa o algoritmo que imaginou para resolver este problema e verifique os resultados comparando com a execução do programa dado na Aula 1.  Teste ambos os programas para pares de números entre  2 147 000 000 e  2 147 483 647, e verifique que o novo algoritmo é mais rápido que o anterior.