cblc/aulas/05-controle/README.org
2025-03-22 11:40:26 -03:00

12 KiB

Curso Básico da Linguagem C

Estruturas de controle de fluxo

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.

Construtos: são os elementos fundamentais da sintaxe e da semântica de uma linguagem.

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

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!

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.

Iterar: da álgebra, o termo significa valer-se do resultado de uma equação, obtido através de cálculos sucessivos.

Sintaxe

for (INICIALIZAÇÃO; CONDIÇÂO; ALTERAÇÃO) {
    BLOCO DE INSTRUÇÕES...
}

Quando houver apenas uma instrução…

for (INICIALIZAÇÂO; CONDIÇÃO; ALTERAÇÃO) INSTRUÇÃO;

for (INICIALIZAÇÂO; CONDIÇÃO; ALTERAÇÃO)
    INSTRUÇÃO;

Loop for infinito…

for (;;) {
    BLOCO DE INSTRUÇÕES...
}

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.

Exemplo do vídeo

#include <stdio.h>

int main(void) {

    for (int i = 0; i < 10; i++) {
        printf("%d - %d\n", i, i * 10);
    }
    
    return 0;
}

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…

:~$ 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

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

while (CONDIÇÃO) {
    BLOCO DE INSTRUÇÕES...
}

Quando houver apenas uma instrução…

while (CONDIÇÃO) INSTRUÇÃO;

while (CONDIÇÃO)
    INSTRUÇÃO;

Loop while infinito…

while (1) {
    BLOCO DE INSTRUÇÕES...
}

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:

int c;
while ((c = getchar()) != '\n' && c != EOF);

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

#include <stdio.h>

int main(void) {
    
    int i = 0;
    while (i < 10) {
        printf("%d", i);
        i += 2;
    }
      
    return 0;
}

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…

:~$ gcc exemplo.c
:~$ ./a.out
0
2
4
6
8

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

do {
    BLOCO DE INSTRUÇÕES...
} while (CONDIÇÃO);

Quando houver apenas uma instrução…

do INSTRUÇÃO;
while (CONDIÇÃO);

do
    INSTRUÇÃO;
while (CONDIÇÃO);

Loop do..while infinito…

do {
    BLOCO DE INSTRUÇÕES...
} while (1);

Exemplo do vídeo

#include <stdio.h>

int main(void) {
    
    int i = 10;
    do
        printf("%d", i);
        i--;
    while (i > 0);
      
    return 0;
}

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…

:~$ gcc exemplo.c
:~$ ./a.out
10
9
8
7
6
5
4
3
2
1

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

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...
}

Quando houver apenas uma instrução nos blocos de instruções…

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

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

#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;
}

Compilando e executando…

:~$ 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

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:

#define elif else if

Por exemplo:

#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);
        }
    }
}

Compilando e executando…

:~$ gcc -Wall fizzbuzz.c
:~$ ./a.out
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

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.

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.

Sintaxe

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...
}

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

#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;
}

Compilando e executando…

:~$ 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

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

CONDIÇÃO ? EXPRESSÃO_SE_VERDADEIRO : EXPRESSÃO_SE_FALSO

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