#+title: Curso Básico da Linguagem C #+subtitle: Aula 5: Estruturas de controle de fluxo #+author: Blau Araujo #+startup: show2levels #+options: toc:3 * Estruturas de controle de fluxo [[https://www.youtube.com/watch?v=9dvDL7FbYKY][Vídeo desta aula]] Construtos da linguagem que alteram a ordem normal da execução do programa que, em princípio, seria linha a linha de cima para baixo. #+begin_quote *Construtos*: são os elementos fundamentais da sintaxe e da semântica de uma linguagem. #+end_quote ** Controle de fluxo A alteração do fluxo normal de execução pode ser produzida por estruturas e alguns construtos da linguagem C. *Estruturas de repetição* - Loop =for= - Loop =while= - Loop =do..while= *Estruturas de decisão* - Estrutura =if..else if..else= - Estrutura =switch..case= *Construtos de desvio incondicional* - =return=: Finaliza a execução de uma função e retorna um valor. - =break=: Interrompe a execução de um =switch= ou de um /loop/. - =continue=: Salta para a próxima iteração de um /loop/. - =goto =: Salta para um rótulo definido como =rótulo:=. #+begin_quote O =goto= torna o código confuso e difícil de entender e manter, levando a um estilo conhecido como /"código espaguete"/, o que deve ser evitado a todo custo, pois isso vai contra os conceitos que definem um código como /estruturado/! #+end_quote ** O loop 'for' O /loop/ =for= uma estrutura de repetição com sintaxe compacta que combina a inicialização de uma variável, a condição de continuidade das repetições e a atualização do valor associado à variável em uma /expressão tripla/, possibilitando a repetição controlada de um bloco de código um número definido de vezes. Com o /loop/ =for=, as repetições são controladas pela /iterações/ com o valor associado a uma variável. #+begin_quote *Iterar*: da álgebra, o termo significa valer-se do resultado de uma equação, obtido através de cálculos sucessivos. #+end_quote *** Sintaxe #+begin_example for (INICIALIZAÇÃO; CONDIÇÂO; ALTERAÇÃO) { BLOCO DE INSTRUÇÕES... } #+end_example Quando houver apenas uma instrução... #+begin_example for (INICIALIZAÇÂO; CONDIÇÃO; ALTERAÇÃO) INSTRUÇÃO; for (INICIALIZAÇÂO; CONDIÇÃO; ALTERAÇÃO) INSTRUÇÃO; #+end_example Loop =for= infinito... #+begin_example for (;;) { BLOCO DE INSTRUÇÕES... } #+end_example #+begin_quote Um loop infinito pressupõe a definição de uma condição de parada no =BLOCO DE INSTRUÇÕES= e o uso de um construto de desvio incondicional, geralmente, o =break=. #+end_quote *** Exemplo do vídeo #+begin_src c #include int main(void) { for (int i = 0; i < 10; i++) { printf("%d - %d\n", i, i * 10); } return 0; } #+end_src *Notas* - A declaração de uma variável =i=, na primeira expressão, faz com que ela seja visível apenas no bloco de instruções. - Ao término das repetições, a variável deixa de existir. - A variável =i= também poderia ser declarada fora da estrutura =for=. - O operação de pós incremento =++=, na terceira expressão, avalia =var= e soma 1 ao seu valor. - A variável =i= poderia ser manipulada de qualquer outra forma na terceira expressão. *Compilando e executando...* #+begin_example :~$ gcc exemplo.c :~$ ./a.out 0 - 0 1 - 10 2 - 20 3 - 30 4 - 40 5 - 50 6 - 60 7 - 70 8 - 80 9 - 90 #+end_example ** Loop 'while' O /loop/ =while= repete a execução de um bloco de instruções /enquanto/ a sua expressão de controle continuar avaliando como verdadeira ou qualquer valor diferente de =0=. Como a continuidade dos ciclos é condicionada à verdade da expressão de controle, o /loop/ =while= pode ser classificado como um /loop condicional/. *** Sintaxe #+begin_example while (CONDIÇÃO) { BLOCO DE INSTRUÇÕES... } #+end_example Quando houver apenas uma instrução... #+begin_example while (CONDIÇÃO) INSTRUÇÃO; while (CONDIÇÃO) INSTRUÇÃO; #+end_example /Loop/ =while= infinito... #+begin_example while (1) { BLOCO DE INSTRUÇÕES... } #+end_example *IMPORTANTE!* Todas as estruturas de repetição podem ser usadas sem a definição de um bloco de instruções ou apenas uma instrução, mas isso só faz sentido em loops onde é possível avaliar condições através de uma expressão, como é o caso dos /loops/ =while= e =do..while=. Por exemplo, este /loop/ é muito utilizado para limpar o conteúdo do buffer de entrada após a digitação de uma entrada do usuário: #+begin_src c int c; while ((c = getchar()) != '\n' && c != EOF); #+end_src Aqui, o objetivo é apenas ler os bytes restante no buffer de entrada, o que pode ser feito na própria expressão de controle do =while=. *** Exemplo do vídeo #+begin_src c #include int main(void) { int i = 0; while (i < 10) { printf("%d", i); i += 2; } return 0; } #+end_src *Nota* Este exemplo reproduz com o /loop/ =while= o mesmo comportamento de um /loop/ =for=, mas sem tornar a variável =i= local ao bloco de instruções: - =int i = 0;= - Inicialização da variável que será iterada. - =i < 10= - Condição de continuidade. - ~i += 2;~ - Alteração da variável =i=. *Compilando e executando...* #+begin_example :~$ gcc exemplo.c :~$ ./a.out 0 2 4 6 8 #+end_example ** Loop 'do..while' Enquanto o /loop/ =while= avalia sua expressão de controle antes de executar o bloco de instruções, o /loop/ =do..while= tem que executar o bloco de instruções pelo menos uma vez antes de avaliar a expressão de controle: fora isso, os funcionamentos são idênticos. *** Sintaxe #+begin_example do { BLOCO DE INSTRUÇÕES... } while (CONDIÇÃO); #+end_example Quando houver apenas uma instrução... #+begin_example do INSTRUÇÃO; while (CONDIÇÃO); do INSTRUÇÃO; while (CONDIÇÃO); #+end_example /Loop/ =do..while= infinito... #+begin_example do { BLOCO DE INSTRUÇÕES... } while (1); #+end_example *** Exemplo do vídeo #+begin_src c #include int main(void) { int i = 10; do printf("%d", i); i--; while (i > 0); return 0; } #+end_src *Notas* - Ainda estamos reproduzindo o comportamento do =for=. - A operação de pós decremento =--= avalia =var= antes de subtrair 1 de seu valor. *Compilando e executando...* #+begin_example :~$ gcc exemplo.c :~$ ./a.out 10 9 8 7 6 5 4 3 2 1 #+end_example ** Estrutura de decisão 'if..else if..else' A estrutura =if..else if..else= (ou simplesmente =if=) possibilita a execução de um bloco de instruções caso a avaliação de um expressão resulte em verdadeira ou em um valor diferente de =0=. *** Sintaxe #+begin_example if (CONDIÇÃO_1) { BLOCO EXECUTADO SE CONDIÇÃO_1 FOR VERDADEIRA... } else if (CONDIÇÃO_2) { BLOCO EXECUTADO SE CONDIÇÃO_2 FOR VERDADEIRA... } else if (CONDIÇÃO_N) { BLOCO EXECUTADO SE CONDIÇÃO_N FOR VERDADEIRA... } else { BLOCO EXECUTADO SE NENHUMA DAS CONDIÇÔES FOR VERDADEIRA... } #+end_example Quando houver apenas uma instrução nos blocos de instruções... #+begin_example if (CONDIÇÃO_1) INSTRUÇÃO SE CONDIÇÃO_1 FOR VERDADEIRA... else if (CONDIÇÃO_2) INSTRUÇÃO SE CONDIÇÃO_2 FOR VERDADEIRA... else if (CONDIÇÃO_N) INSTRUÇÃO SE CONDIÇÃO_N FOR VERDADEIRA... else INSTRUÇÃO SE NENHUMA DAS CONDIÇÔES FOR VERDADEIRA... #+end_example *Notas* - Apenas o bloco =if= é obrigatório. - É possível escrever quantos blocos =else if= forem necessários. - Só podemos escrever um bloco =else=, se necessário. - A estrutura =if= termina assim que um dos seus blocos é executado. - Eu prefiro me referir aos blocos de instruções como /consequências/. *** Exemplo do vídeo #+begin_src c #include int main(void) { for (int i = 0; i <= 20; i++) { if (i % 2 == 0) { printf("%d é par\n", i); } else if (i % 15 == 0) { printf("%d é ímpar e divisível por 3 e 5\n", i); } else { printf("%d é ímpar\n", i); } } return 0; } #+end_src *Compilando e executando...* #+begin_example :~$ gcc exemplo.c :~$ ./a.out 0 é par 1 é ímpar 2 é par 3 é ímpar 4 é par 5 é ímpar 6 é par 7 é ímpar 8 é par 9 é ímpar 10 é par 11 é ímpar 12 é par 13 é ímpar 14 é par 15 é ímpar e divisível por 3 e 5 16 é par 17 é ímpar 18 é par 19 é ímpar 20 é par #+end_example *** Dica: 'else if' abreviado A escrita do bloco =else if= pode ser um pouco verbosa demais para quem escreve muitos scripts em Bash, que usa =elif=. Não existe uma abreviação nativa em C, mas nós podemos implementá-la com uma macro: #+begin_src c #define elif else if #+end_src Por exemplo: #+begin_src c #include #define elif else if int main(void) { for (int i = 1; i <= 15; i++) { if (!(i % 15)) { puts("FizzBuzz"); } elif (!(i % 3)) { puts("Fizz"); } elif (!(i % 5)) { puts("Buzz"); } else { printf("%d\n", i); } } } #+end_src *Compilando e executando...* #+begin_example :~$ gcc -Wall fizzbuzz.c :~$ ./a.out 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz #+end_example ** Estrutura 'switch..case' A estrutura =switch..case= possibilita selecionar a execução de um ou mais blocos de instruções diferentes com base na correspondência entre o valor de uma expressão e os valores especificados nas cláusulas =case=. #+begin_quote *Cláusulas*: seções de uma estrutura sintática que definem condições ou comportamentos específicos dentro da totalidade do comando de que fazem parte. #+end_quote *** Sintaxe #+begin_example switch (CONTROLE) { case CONST_1: BLOCO DE INSTRUÇÕES... [break;] case CONST_2: BLOCO DE INSTRUÇÕES... [break;] case CONST_N: BLOCO DE INSTRUÇÕES... [break;] default: BLOCO DE INSTRUÇÕES... } #+end_example *Notas* - A expressão =CONTROLE= deve avaliar um tipo compatível com =int=. - Os valores nas cláusulas =case= devem ser /expressões constantes/ ou expressões ou valores que possam ser resolvidos em tempo de compilação. - Sejam quais forem os valores nas cláusulas =case=, eles devem ser do mesmo tipo da expressão =CONTROLE=. *** Exemplo do vídeo #+begin_src c #include int main(void) { int flag; for (int i = 1; i <= 20; i++) { flag = 0; flag += (i % 2) ? 0 : 1; flag += (i % 3) ? 0 : 2; switch (flag) { case 1: printf("%d é divisível por 2\n", i); break; case 2: printf("%d é divisível por 3\n", i); break; case 3: printf("%d é divisível por 2 e 3\n", i); break; default: printf("%d\n", i); } } return 0; } #+end_src *Compilando e executando...* #+begin_example :~$ gcc exemplo.c :~$ ./a.out 1 2 é divisível por 2 3 é divisível por 3 4 é divisível por 2 5 6 é divisível por 2 e 3 7 8 é divisível por 2 9 é divisível por 3 10 é divisível por 2 11 12 é divisível por 2 e 3 13 14 é divisível por 2 15 é divisível por 3 16 é divisível por 2 17 18 é divisível por 2 e 3 19 20 é divisível por 2 #+end_example *** Bônus: expressão condicional (ternária) A linguagem C implementa a avaliação condicional de expressões na forma de uma /expressão ternária/ (uma expressão com 3 termos): #+begin_example CONDIÇÃO ? EXPRESSÃO_SE_VERDADEIRO : EXPRESSÃO_SE_FALSO #+end_example Aqui, se a expressão =CONDIÇÃO= avaliar verdadeiro ou um valor diferente de =0=, o valor de toda a expressão ternária será o valor avaliado no seu segundo termo (depois de =?=); se avaliar falso ou igual a =0=, a expressão ternária terá o valor avaliado no seu terceiro termo (depois de =:=).