#+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 <rótulo>=: 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 <stdio.h>

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 =<var>++=, 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 <stdio.h>

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 <stdio.h>

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 =<var>--= 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 <stdio.h>

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 <stdio.h>

#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 <stdio.h>


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 =:=).


