Laboratório de Programação Prof. Oscar Luiz Monteiro de Farias [email protected] Capítulo 3 – Fluxo de Controle Os comandos de fluxo de controle especificam a ordem em que a computação é realizada. Expressões, quando seguidas por um ; tornam-se um comando: x = 0; i++; printf(...); As chaves { e } agrupam declarações e comandos em um comando composto ou bloco, e são sintaticamente equivalentes a um único comando. Variáveis podem ser declaradas dentro de quaisquer blocos. O comando if-else (1) Usado para expressar decisões. Sintaxe: if (expression) statement1 else statement2 onde a parte do else é opcional. expression é avaliada. Se for verdadeira (!= 0), statement1 é executado. Se for falsa (== 0) e se houver a parte else, statement2 é executado. O comando if-else (2) Como o else é opcional , há uma ambigüidade quando um else é omitido em uma seqüência de comandos if-else aninhados. Solução: o else é sempre associado com o mais recente if sem else. if (n > 0) if (n > 0) { if (a > b) z = a; if (a > b) != z = a; else } z = b; else z = b; O comando else-if (1) if (condition1) /* para decisões múltiplas */ statement1 else if (condition2) statement2 ... ... else statementn+1 O comando else-if (2) As condições são avaliadas em ordem, a partir do topo, até que uma das condições (conditioni )seja satisfeita; nesse ponto statementi é executado e a construção inteira é terminada. (statementi pode representar vários statements envolvidos por chaves). Se nenhuma das condições for satisfeita, o statementn+1 após o else é executado, se houver. Se o else e statementn+1 forem omitidos nenhuma ação é tomada. A última parte else corresponde ao caso “nenhuma das anteriores” ou caso default, quando nenhuma das condições é satisfeita. O comando else-if (3) Quando não há ação explícita para o caso default, o else statementn+1 pode ser omitido, ou usado para recuperação de erro, capturando uma condição “impossível”. Ver exemplo binsearch → prog25-chap03-pg52.c Alternativa para decisões múltiplas: o comando switch. O comando switch (1) switch (expression) { case const-expr1: statements … case const-exprn: statements default: statements } O comando switch (2) Cada case é rotulado por um ou mais constantes de valor inteiro ou expressões constantes. Se um case corresponde aovalor de expression, a execução tem início neste case. Todas as case expressions devem ser diferentes umas das outras. O case rotulado como default é executado se nenhum dos outros cases for satisfeito. O case default é opcional; se não estiver presente e nenhum dos cases combinar com expression, nenhuma ação é realizada. Os cases and a cláusula default podem ocorrer em qualquer ordem. Ver prog26-chap03-pg52.c O comando switch (3) O comando break causa uma saída imediata do switch. Após o término da execução do código associado a um case, a execução prossegue no código associado ao case que se segue. break e return são as formas mais comuns de se abandonar um switch. O comando break também pode ser usado para forçar uma saída imediata de loops (while, for e do) Loop while while (expression) statement expression é avaliada. Se for diferente de zero (verdadeira), statement é executado. Este ciclo continua até expression se tornar zero (falsa), prosseguindo a execução após statement. statement i pode representar vários statements envolvidos por chaves. Loop for (1) for (expr1; expr2; expr3) statement É equivalente a: expr1; while (expr2) { statement expr3; } Exceção: quando emprega o comando continue. Loop for (2) Gramaticamente os três componentes do loop for são expressões. Normalmente expr1 e expr3 são atribuições ou chamadas de função e expr2 é uma expressão relacional. Qualquer uma das tres partes pode ser omitida, embora os ; devam permanecer. Se expr2 não estiver presente, é assumida como permanentemente verdadeira. for (;;) { ... } /* representa um loop infinito */ Loop for (3) for (i = 0; i < n; i++){ … } O índice e o limite de um loop for em C podem ser alterados dentro do loop. A variável de índice i retém o seu valor quando o loop termina por algum motivo. Exemplo: atoi - prog27-chap03-pg54.c atoi converte uma cadeia de caracteres em seuequivalente numérico. Versão mais feral do que a do chap02; considera espaços iniciais e um sinal opcional de + ou -. Algoritmo de atoi: Salta espaços em branco, se existirem obtém o sinal, se houver obtém a parte inteira e a converte A biblioteca padrão prove a função strtol, mais elaborada, para a conversão de strings em inteiros longos; ver Section 5 do Appendix B. Algoritmo de ordenação Shell(1) Nos algoritmos de ordenação que movem os ítens uma posição de cada vez, o tempo de execução médio será, no melhor dos casos proporcional a N2, uma vez que cada ítem se deslocará em média (1/3)N posições durante o processo de classificação. Melhorias substanciais relativas à inserção direta podem ser conseguidas se ítens distantes puderem ser trocados, ao invés de apenas os ítens próximos. Algoritmo de ordenação Shell(2) i) divide-se os 16 registros em oito grupos de 2 cada (l1): (R1, R9), (R2, R10), ...(R ,R ); Classifica- Shell sort O operador , (1) Mais usado no comando for. Um par de expressões separadas por , é avaliado da esquerda para a direita, e o tipo e o valor do resultado são o tipo e o valor do operando direito. Em um comando for é possível colocar múltiplas expressões em seus componentes, por exemplo, para processar dois índices em paralelo. Exemplo: prog29-chap03-pg55.c – função reverse, que inverte a cadeia de strings s. Obs.: as vírgulas que separam argumentos de funções e variáveis em declarações não são operadores. O operador , (2) Uma expressão com vírgula também poderia ser usada para a atroca de elementos em reverse, onde a troca seria feita em uma única operação. for (i = 0, j = strlen(s)-1; i < j; i++, j--) c = s[i], s[i] = s[j], s[j] = c; Loop do-while (1) O do-while testa a condição de término ao final do loop, após executar o corpo do loop, ao contrário dos loops while e for, que testam a condição de término no início. Sintaxe: do statement while (expression); statement é executado e expression é avaliada. Se for verdadeira statement é executado novamente, e assim por diante. Se expression se torna falsa o loop finda. Loop do-while (2) Exemplo: prog30-chap03-pg56.c – função itoa, que converte um número para uma cadeia de caracteres ( o inverso de atoi). A cadeia será gerada na ordem reversa e, então, invertida. O do-while é necessário ou, ao menos conveniente, pois no mínimo um caracter deve ser instalado no vetor s, mesmo que n seja zero. Usou-se chaves ao redor do único comando que constitui o corpo do do-while, para evitar confusões como o início de um loop while. break O comando break permite uma saída imediata de um for, while e do-while, e de um switch. break faz com que o loop (ou switch) mais interno seja terminado imediatamente. Exemplo: prog31-chap03-pg57.c – função trim, que remove espaços, tabulações e caracteres de new line do final de um string, usando um break para sair do loop, quando o caracter mais à direita do string coincidir com um dos caracteres anteriormente mencionados. continue O comando continue é relacionado com o break. continue inicia a próxima iteração do loop for, while, ou do-while. No while e do-while isto significa que a parte do teste será executada imediatamente. No for, o controle passa para a parte de incremento (avalia expr3). continue aplica-se só a loops e não a switch . continue – exemplo de uso Código para processar apenas elementos positivos no vetor “a”; valores negativos são saltados. for (i = 0; i < n; i++) { if (a[i] < 0) /* skip negative elements */ continue; ... /* do positive elements */ } goto e labels (1) Formalmente pode-se prescindir do uso do goto, pois sempre existe formas de se escrever um código equivalente. Usado indiscriminadamente torna o código confuso e não linear. Todavia, existem situações em que pode ser útil. Abandonar todos os loops, em uma estrutura de loops profundamente aninhada. Um label segue a mesma sintaxe de umavariável e é seguido por um sinal de : goto e labels (2) for ( ... ) for ( ... ) { ... if (disaster) goto error; } ... error: /* clean up the mess */ ... goto e labels (3) Determinar se dois vetores a e b possuem um elemento em comum. for (i = 0; i < n; i++) for (j = 0; j < m; j++) if (a[i] == b[j]) goto found; /* didn't find any common element */ ... found: /* got one: a[i] == b[j] */ ... goto e labels (4) Código alternativo sem o uso do goto. found = 0; for (i = 0; i < n && !found; i++) for (j = 0; j < m && !found; j++) if (a[i] == b[j]) found = 1; if (found) /* got one: a[i-1] == b[j-1] */ ... else /* didn't find any common element */ ...