Controle de fluxo em C

Execução sequencial

 command_1 ; command_2 ; command_3 ; command_4 ;

Um comando termina no ponto-e-vírgula; isso significa que todos os efeitos do comando serão sacramentados quando a "execução do código fonte" chegar no ponto-e-vírgula. O trecho de código acima consiste da execução, um após o outro, de quatro comandos.

A formatação abaixo é equivalente àquela da linha acima, embora a formatação com um comando por linha seja geralmente mais fácil de ler.

 command_1;
 command_2;   // isso é um comentário que vai até o fim da linha
 command_3;   /* isso é um comentário que termina em qualquer posição */
 command_4;

Não esqueça do ponto-e-vírgula ao final de cada comando. Eu prefiro não deixar espaço em branco entre a última letra do comando e o ponto-e-vírgula; a linguagem aceita as duas formas.

Um comando simples pode ser uma atribuição como a abaixo. Em C, a atribuição é denotada por '=' (igual).

 a = b + c;

Agrupamento de comandos em blocos

Comandos podem se logicamente agrupados "cercando" o bloco de comandos com par de chaves. O par de chaves é equivalente ao BEGIN ... END; do Pascal.

  { command_1 ; command_2 ; command_3 ; command_4 ; }

Decisões

Comando if

if (expression)
   command ;

ou com um bloco de comandos:

if (expression)
{
   command_1 ;
   command_2 ;
   ...
}

Ao contrário de Pascal, a condição do comando "if" pode testar qualquer expressão cujo resultado possa ser avaliado para um número, sendo que 0 (zero) é considerado falso e não-zero é verdadeiro.

Contar com que verdadeiro seja qualquer coisa não-zero é uma má ideia porque pode levar a erros difíceis de detectar, e portanto deve ser evitado.

Os if's podem ser aninhados da maneira óbvia:

if (expression1)
   if (expression2)
   {
     command_1 ;
     command_2 ;
     ...
   }

Note que os comandos estão endentados (afastados da margem esquerda) para facilitar a leitura. A linguagem C permite formatação livre, mas endentar o código é fundamental para tornar o código legível.

O comando abaixo é logicamente equivalente ao exemplo acima, com a adição das chaves para "cercar" o bloco interno. Sempre que facilitar a leitura, empregue chaves para delimitar um bloco de código.

if (expression1)
{
   if (expression2)
   {
      command_1 ;
      command_2 ;
      ...
   }
}

Comando if-else

No comando if-else as duas cláusulas são mutuamente exclusivas.

if (expression)
   command1 ; // <--- there has to be a semicolon here!
else
   command2 ;

ou, com blocos de comandos:

if (expression)
{
   command_1if ;
   command_2if ;
   command_3if ;    
}
else
{
   command_1else ;
   command_2else ;
}

If-else podem ser aninhados:

if (expression_1)
   if (expression_2)
      command_1 ;
   else
      command_2 ;
else
   command_3 ;

Ou, agrupando com chaves:

if (expression_1)
{
   if (expression_2)
      command_1 ;
   else
      command_2 ;
}
else
{
   command_3 ;
}

A cláusula ''else'' sempre se combina com o último ''if'' em aberto; caso se deseje forçar outra combinação, deve-se usar chaves {...} para organizar a abertura/fechamento das cláusulas dos "if's".

Comando switch

A expressão deve avaliar para um valor inteiro. Este inteiro deve corresponder a uma das cláusulas ("case"); se o resultado da avaliação não corresponder a nenhuma das cláusulas explícitas, então a cláusula "default" é a escolhida.

Se uma cláusula não for terminada por um "break", os comandos da cláusula seguinte são executados.

switch (expression)
{
  case constant_1:
       command_1 ;
       ...
       break ;

  case constant_2:
       command_2 ;
       ...
       break ;

  case constant_3:    // duas constantes para um mesmo bloco de comandos
  case constant_4:
       command_3_4 ;
       ...
       break ;

  ...

  default:           // se o resultado não "casou" antes, executa estes
       command_Na;   //   comandos
       ...
       break ;
}

Os (vários) comandos de cada cláusula não precisam ser cercados por chaves; o bloco termina no primeiro "break" que for encontrado.

Exemplo: este comando conta o número de carros dentre quatro possibilidades:

ch = getchar();  // lê um caractere que identifica o tipo do carro

switch(ch)       // escolhe um dentre os tipos possíveis
{
  case ‘C’:      // duas possibilidades, C e c
  case ‘c’:
    corsa++ ;
    break ;

  case ‘P’:
    prisma++ ;
    break ;

  case ‘p’:      // maiúsculas e minúsculas são distintas
    palio++ ;
    break ;

  default:
    outros++ ;
    break ;
}

Laços

Comando while

A expressão é avaliada, e se o resultado for diferente de zero (FALSO) então os comandos do corpo do "while" são executados.

while (expression)
   command ;

ou

while (expression)
{
   command_1 ;
   command_2 ;
   ...
}

Exemplo:

i = 0 ;                         // inicializa a variável de controle
while (i < 100)                 // testa condição de término 
{
   printf ("i vale %d\n", i) ;  // computa algo com a variável de controle
   i++ ;                        // atualiza a variável de controle
}

O corpo do comando while é executado zero ou mais vezes.

Comando do-while

O corpo do laço é executado e a expressão é avaliada. Se o resultado for diferente de zero, então o corpo do laço é executado novamente.

do
   command ;
while (expression) ;

ou

do
{
   command_1 ;
   command_2 ;
   ...
}
while (expression) ;

Exemplo:

i = 100 ;                       // inicializa a variável de controle
do
{
   printf ("i vale %d\n", i) ;  // computa algo com a variável de controle
   i-- ;                        // atualiza a variável de controle
}
while (i > 0) ;                // testa condição de término

O corpo do comando do-while é executado uma ou mais vezes.

Comando for

O comando "for" é o canivete suiço da linguagem C, e é logicamente equivalente ao laço

i = 0 ;           // inicializa as variáveis de controle
while (i < 100)   // teste da condição
{
   ... ;         // computa algo com a variável de controle
   i++ ;         // atualiza as variáveis de controle
}

O comando "for" é uma forma compacta de representar o laço acima:

for (initializção ; teste_da_condição ; atualização)
  command ;

Note os ponto-e-vírgula que separam as três "seções" do comando.

ou

for (initialização ; teste_da_condição ; atualização)
{
   command_1 ;
   command_2 ;
   ...
}

Exemplo: laço de repetição com a variável de controle indo de 0 a 99:

for (i = 0; i < 100; i = i + 1)
   printf ("i vale %d", i) ;

O exemplo acima pode ser reescrito como:

i = 0 ;                     // inicialização
while (i < 100)             // condição
{
  printf ("i vale %d", i) ;
  i++ ;                     // atualização
}

Uma forma peculiar uso do ''for'' é o laço infinito, que não tem inicialização, nem condição e nem atualização:

for (;;)      // while (TRUE) { }; 
{
}

Desvios

O material que segue será estudado nas próximas aulas.

Comando return

O comando ''return'' encerra a função atual e retorna ao ponto de onde ela foi invocada. Um "return" pode aparecer em qualquer local da função, não necessariamente no final dela.

Exemplo:

int compara (int a, int b)
{
  if (a > b) return  1 ;
  if (a < b) return -1 ;
  if (a == b) return  0 ;
}

Comando exit

A função ''exit (int status)'' é provida pela biblioteca de sistema ''stdlib.h'' e permite o encerramento do programa. O status informado como argumento é devolvido ao sistema operacional, mais especificamente à shell que lançou a aplicação.

exit (0) ;

Comando break

O comando ''break'' só é usado dentro de blocos ''switch'', ''do'', ''while'' ou ''for''. Ao ser acionado, ele salta a execução para o primeiro comando após o bloco atual.

Exemplo:

for (;;)
{
  printf ("Aceita? (s/n) ") ;
  resp = getchar() ;
  if(resp == 's' || resp == 'n')
    break;
  printf ("\n") ;
}
// o break salta para cá 

O comando ''break'' deve ser usado com muito cuidado, pois ele pode tornar o fluxo de execução muito complexo (e portanto sujeito a erros). Como regra geral, evite utilizá-lo, a não ser que seja absolutamente necessário.

Comando continue

O comando ''continue'' é usado em laços ''for'', ''do'' e ''while''. Ele interrompe a iteração atual e inicia a próxima iteração do laço onde se encontra.

Exemplo:

int i;

for (i = -10; i < 10; i++)
{
  if (i == 0)
    continue; // pula para a próxima iteração
  printf("%f\n", 15.0/i);
  // ...
}

O comando ''continue'' deve ser usado com muito cuidado, pois ele pode tornar o fluxo de execução muito complexo (e portanto sujeito a erros). Como regra geral, evite utilizá-lo, a não ser que seja absolutamente necessário.

Comando goto

O comando ''goto'' permite saltar a execução para locais definidos por rótulos ou etiquetas (//labels//), como mostra o exemplo a seguir:

goto PARTE2;

// ... (qualquer código)

PARTE2:
i = 0 ;
// ...  

O comando ''goto'' deve ser usado com muito cuidado, pois ele pode tornar o fluxo de execução muito complexo (e portanto sujeito a erros). Como regra geral, evite utilizá-lo, a não ser que seja absolutamente necessário.

Comando assert

''assert'' é uma macro que verifica uma asserção (condição) e encerra o programa se ela for falsa (nula). Ao encerrar, uma mensagem de erro indicando a localização da asserção violada é gerada, o que facilita localizar problemas.

#include <assert.h>

int main()
{
  ...
  assert (i >= 0) ;
  ...
}

Se a condição i >= 0 for falsa, o programa será interrompido e a seguinte mensagem será gerada, indicando o arquivo (''program.c''), a linha (6), a função (''main'') e a asserção violada:

a.out: program.c:6: main: Assertion `i >= 0' failed.

Exercícios

Em C, a atribuição é '=' (igual) enquanto que a comparação de igualdade é '==' (igual igual).

O operador de conjunção (AND) de duas comparações é '&&' (ampersand ampersand).

O operador de disjunção (OR) de duas comparações é '||' (vertical vertical ou pipe pipe).



Para resolver os exercícios, copie o trecho de código abaixo para um arquivo chamado teste.c, edite-o e então compile este arquivo com o comando

gcc -Wall teste.c -o teste
./teste

Recorte nas linhas.


/* Esqueleto para os exercícios */

#include <stdio.h>              // inclusão de definições

int main (void)                 // função principal
{
   int a, b, c;                 // declaração das variáveis

   a = 10;                      // valores para os lados dos triângulos
   b = 10;
   c = 10;

   if (a == b) && (b == c)      // (a igual b) AND (b igual c)
   {
      printf ("equilatero: %d %d %d\n", a, b, c);  // imprime resultado na tela
   }

  return (0);                    // retorno (fim) da função
}

Escreva trechos de programas para: