Departamento de Engenharia Electrotécnica PROGRAMAÇÃO DE MICROPROCESSADORES 2007 / 2008 Mestrado Integrado em Engenharia Electrotécnica e Computadores 1º ano 1º semestre Testes, condições e ciclos http://tele1.dee.fct.unl.pt Luis Bernardo
1 Introdução Nos capítulos 3 e 4 do livro Linguagem C de Luís Damas, recomendado para a disciplina de Programação de Microprocessadores, são apresentados respectivamente os testes e condições, e os ciclos. Esta aula visa consolidar estas matérias através de um conjunto de exercícios. Os primeiros quatro exercícios têm como objectivo complementar o estudo realizado na aula teórica. O último exercício é avaliado pelo docente. GUARDE O CÓDIGO desenvolvido na memória USB. Durante a próxima aula o docente pode pedir-lhe para mostrar o código desenvolvido para este exercício, e pode fazer-lhe algumas perguntas. O método de trabalho é nos primeiros exercícios, editar os ficheiros fontes com o código proposto, e realizar as modificações pedidas, seguindo as instruções do enunciado. No último trabalho é resolver o problema proposto, aplicando os conceitos aprendidos nesta secção da matéria. 2 Exercícios de aprendizagem 2.1 TESTES E CONDIÇÕES Ao contrário de outras linguagens, a linguagem C não define um tipo específico para guardar valores Booleanos (Verdadeiro/Falso). É utilizado o tipo inteiro, onde Falso é representado pelo valor 0 (zero), e verdadeiro, por qualquer valor diferente de 0. Os valores dos tipos básicos apresentados nos exercícios da última aula podem ser comparados utilizando-se as operações: > (maior), >= (maior ou igual), < (menor), <= (menor ou igual), == (igual), e!= (diferente). Os valores Booleanos podem ser combinados utilizando as operações && (AND), (OR), ou! (Negação). As condições Booleanas podem ser testadas, de forma a afectar a sequência do programa executado. A instrução base é a sequência if (cond) then-expr1; else expr2; ou simplesmente if (cond) then-expr1;. Caso se queiram utilizar várias instruções em expr1, e expr2, é necessário colocar estas expressões entre chavetas {. Existem duas formas compactas de teste, baseadas na instrução switch, e no operador ternário?:, com os significados representados na figura abaixo. Deve-se sempre indentar o código dentro de chavetas, e dentro de uma instrução if, else, ou switch, de forma a o tornar legível. switch (var) { case valor1: expr1; break; case valor2: expr2; break; default: exprd; é equivalente a var= cond? expr1 : expr2; é equivalente a if (var == valor1) { expr1; else if (var == valor2) { expr2; else { exprd; if (cond) var= expr1; else var= expr2; EXERCÍCIO 1: Crie um ficheiro novo com o nome nota.c e introduza o código apresentado de seguida, omitindo os comentários (entre /* e */). Este código pretende determinar a nota final à disciplina de Programação de Microprocessadores. No entanto, falta realizar duas regras: a) Um aluno com duas faltas só pode ter no máximo 14 na 2
nota de laboratório; b) Um aluno com três faltas só pode ter no máximo 12 valores na nota final. Complete o código com os testes necessários! /* * Exercício 1 - cálculo da nota final de PM * Ficheiro: nota.c */ #include <stdio.h> main() { /* Dados do aluno */ unsigned int nota_teorica= 0, nota_pratica= 0, faltas= 0; float media; /* média final */ unsigned int nota_final; /* nota final */ printf("cálculo da nota final a PM\n\n"); printf("introduza os seguintes dados:\n"); printf("\tnúmero de faltas nos trabalhos práticos: "); scanf("%d", &faltas); printf("\tnota dos trabalhos práticos: "); scanf(" %d", ¬a_pratica); printf("\tnota dos testes e exames: "); scanf(" %d", ¬a_teorica); /* Calcula média */ if (faltas > 3) { printf("reprovou à disciplina por faltas\n"); else { /* EXERCÍCIO: CORRIJA O QUE FALTA AQUI! */ media= 0.5*nota_pratica + 0.5*nota_teorica; /* Arredonda real a 0.5 e converte para int! */ nota_final= (int)(media+0.5); printf("aprovado com a nota final de %d valores (%.1f)\n", nota_final, media); /* Escreve só 1 casa decimal */ 2.2 CICLOS A instrução while é usada para repetir a realização de outra instrução (ou grupo de instruções entre {) enquanto uma condição se mantiver verdadeira. Existem depois, outras duas instruções, for e do-while, que são formas compactas de while especialmente dedicadas respectivamente a percorrer um conjunto finito de valores, e a realizar um ciclo onde a expressão é sempre realizada uma vez. for (init; cond; pos-inst) while(cond); é equivalente a é equivalente a init; while (cond) { pos-inst; while(cond) { Um ciclo tem geralmente uma variável de controlo do tipo inteiro que percorre uma gama de valores, ou uma variável Booleana, que determina quando se sai do ciclo. 3
No primeiro caso, existe a instrução var++ especialmente destinada a incrementar em uma unidade um inteiro ou carácter, ou a instrução var += valor que realiza um incremento de valor unidades sobre a variável var. Também se pode sair do ciclo com a instrução break, ou saltar para a próxima iteração do ciclo com a instrução continue. O código seguinte exemplifica a utilização do ciclo for para a variável i percorrer todos os inteiros entre 0 e i-1, escrevendo uma tabela com os valores de i 2. /* * Exercício 2 - Tabela de n^2 * Ficheiro: ciclo.c */ #include <stdio.h> #include <stdlib.h> /* define exit() para sair do programa */ main() { int i, n=0; long long int res; printf(" TABELA n²\n"); printf("introduza o número de elementos pretendidos: "); if (scanf(" %d", &n)!= 1) { /* Se leu um elemento */ printf("número de elementos inválido\n"); exit(1); /* Sai do programa */ for (i= 0; i<n; i++) { /* i tem valores entre 0 e n-1 */ res= i*i; /* Quadrado do número */ printf("\t%d\t%lld\n", i, res); EXERCÍCIO 2: Crie um ficheiro novo com o nome ciclo.c e introduza o código apresentado na figura anterior, omitindo os comentários (entre /* e */). De seguida modifique o código para escrever a sequência de números de Fibonacci, que têm a 0 Se i == 0 seguinte definição: F ( i) = 1 Se i == 1. F( n 1) + F( n 2) Se i > 1 Sugere-se que declarem duas variáveis auxiliares (Fn1, Fn2) do mesmo tipo de res, para guardar respectivamente os valores de F(n-1) e F(n-2). Depois, dentro do ciclo, teste o valor de i, aplicando a expressão correcta da fórmula. Não se esqueça de actualizar o valor de Fn1 e Fn2 dentro de cada passo do ciclo, para a próxima iteração. EXERCÍCIO 3: Crie um ficheiro novo com o nome substituicao.c e introduza o código apresentado na figura abaixo, omitindo os comentários (entre /* e */). Este código realiza um codificador de substituição (apenas para os caracteres 0, 1 e 2 ), que troca letra a letra uma mensagem, segundo uma chave introduzida pelo utilizador. Este código ilustra a utilização do ciclo do-while para ler cada elemento da chave, garantindo-se que não há letras duplicadas na chave. O getchar a seguir ao ciclo remove o \n que é deixado no buffer de leitura após o número introduzido. Depois, entra num ciclo onde lê a mensagem carácter a carácter sequencialmente e codifica-o utilizando a instrução switch para seleccionar a chave correcta. Desta forma, usa-se o fim de linha da mensagem para parar a leitura ( \n ). Repare que a variável fim funciona como variável de controlo do ciclo, sendo colocada a TRUE quando é lido o primeiro carácter 4
diferente de 0, 1 ou 2. Por outro lado, a variável primeiro_caracter é usada para garantir que se escreve apenas uma vez a mensagem A mensagem codificada é:. Pretende-se que modifique o codificador, de forma a codificar os caracteres de 0 a 5, mantendo o mesmo modo de funcionamento: ler a chave e depois ler uma mensagem e codificá-la. /* * Exercício 3 - Cifra substituição * Ficheiro: substituicao.c */ #include <stdio.h> main() { const int FALSE= 0; /* constante Booleana FALSO */ const int TRUE=!FALSE; /* constante Booleana VERDADEIRO */ char k0,k1,k2; /* Guardam chave de '0', '1' e '2' */ char msg, cod; /* Variáveis para guardar mensagem */ int primeiro_caracter= TRUE;/* Controlar 1ª escrita de char */ int fim= FALSE; /* Controlar fim de leitura de mensagem */ /* Ler chave */ printf("introduza a chave da cifra :\n"); printf("'0'="); scanf(" %c", &k0); while((k0<'0') (k0>'2'));//se não estiver entre '0' e '2' printf("'1'="); scanf(" %c", &k1); while((k1<'0') (k1>'2') (k1==k0)); /* ou igual a k0 */ printf("'2'="); scanf(" %c", &k2); while((k2<'0') (k2>'2') (k2==k0) (k2==k1)); /* */ getchar(); /* Retira '\n' do buffer de leitura */ /* Ciclo de leitura de mensagem */ printf("introduza a mensagem a cifrar: "); while (!fim) { msg= getchar(); /* Lê próximo carácter sem saltar espaço */ /* é equivalente a scanf("%c", &msg); */ switch(msg) { case '0': cod= k0; break; case '1': cod= k1; break; case '2': cod= k2; break; default : fim= TRUE; /* Carácter inválido */ if (primeiro_caracter) { /* Só corre uma vez; antecedendo a mensagem cifrada */ printf("\na mensagem codificada é: "); primeiro_caracter= FALSE; /*para não tornar a correr */ if (!fim) /* Se não está a terminar escreve */ putchar(cod); /* o valor codificado */ /* putchar(cod) é equivalente a printf("%c", cod) */ putchar('\n'); /* Muda de linha */ 5
3 Exercício final Pretende-se desenvolver um programa para ler valores de tempo no formato h(hora) m(minuto) s(segundo), devolvendo o número de segundos associado ao tempo introduzido. No entanto, o utilizador deve poder omitir campos (e.g. introduzir só h23 ). Para terminar a leitura o utilizador pode introduzir um carácter diferente de h, m ou s. Pretende-se também que o programa valide os valores introduzidos, não deixando introduzir valores de minutos ou segundos superiores a 59, nem introduzir campos repetidos (só se pode introduzir 0 ou 1 campo hora; 0 ou 1 campo minuto; 0 ou 1 campo segundo). Repare que: São necessárias variáveis para guardar cada componente do tempo; Só há leitura da parte numérica de uma componente do tempo se o carácter for válido; Dependendo do carácter lido, o valor numérico contribui para o valor final de tempo com um peso diferente; O ciclo de leitura de componentes do tempo não tem um número de componentes fixo, nem uma ordem fixa. Quando terminar o exercício, chame o docente e mostre a aplicação a funcionar durante a aula. Apresenta-se na figura seguinte um exemplo de utilização com a aplicação pretendida: m56 h37 s23 x Leu 37h 56m 23s = 136583 segundos h1 x Leu 1h 0m 0s = 3600 segundos h25 m70 x Erro: número de minutos inválidos h25 h70 x Erro: campo hora duplicado 6