Universidade Federal de Minas Gerais
Instituto de Ciências Exatas
Departamento de Ciência da Computação

Algoritmos e Estruturas de Dados I

Estruturas & comandos para controle do fluxo de execução

 

Seqüência

Na linguagem C o fluxo de controle passa sequencialmente de um comando para o seguinte na ordem convencional de leitura. Este é o tipo de estruturação mais simples do fluxo de controle e é denominado seqüência. Considere  o seguinte trecho de programa:

int a=2;
int b=3;
int c=4;


Quando o fluxo de controle atinge a sentença int a=2; ocorre sua execução, em seguida ou na seqüência o fluxo de controle se dirige para a sentença int b=3; e ocorre sua execução e na seqüência o fluxo de execução atinge a sentença int c=4;

Blocos

Na linguagem C podemos agrupar uma seqüência de comandos através do uso de abre e fecha chaves - {}. Os comandos assim agrupados passam a formar o que é denominado um bloco. Quando o fluxo de controle atinge um bloco cada um dos comandos que formam o bloco são executados em seqüência. O uso de blocos é especialmente importante no caso de comandos condicionais e iterativos.

Comando se-então-senão

Nas linguagens de alto-nível existem vários tipos de comandos denominados comandos condicionais que permitem ao programador expressar que, em função do resultado de uma expressão, ele quer que o fluxo de controle vá para diferentes grupos de comandos. Na linguagem C um destes comandos é o comando if. O comando if tem a seguinte forma:
if(<expressão> ) <comando1>; else <comando2>;
Quando o fluxo de controle atinge o comando if:

  1. a <expressão> é avaliada
  2. se a <expressão> resultar no valor true  (valor diferente de zero) o fluxo de controle passa para o  <comando1> e após a execução do <comando1> o fluxo de controle deixa o comando if.
  3. se a <expressão> resultar no valor false (valor igual a zero) o fluxo de controle passa para o <comando2> e após a execução do <comando2> o fluxo de controle deixa o comando if.


Ou seja, dependendo do valor da
<expressão> , o fluxo de controle irá para <comando1> ou <comando2 de forma mutuamente exclusiva.
Abaixo são mostradas algumas formas do comando if:
if(<expressão>) <comando1>; /* não tem a cláusula else */
A cláusula
else é opcional. Quando o fluxo de controle atinge um comando if que não contém a clausula else, o fluxo de controle passa para a avaliação da expressão. Se o resultado final da avaliação da expressão for (diferente de zero) true o fluxo de controle passa para o <comando1> e após a execução do <comando1> o fluxo de controle deixa o comando if. Se a expressão for (zero) false o fluxo de controle deixa o comando if e o único efeito foi a avaliação da expressão.

if(<expressão>) { <c1>; <c2>; <c3>;} /* uso do bloco */
Quando queremos executar vários comandos juntos ao “então” ou vários comandos juntos ao “senão” temos que usar o bloco - {}, para agrupá-los.
 A seguir exemplificamos com alguns trechos de programa o uso do comando se-então-senão na linguagem C

Obter o valor absoluto da variável x:

if(x<0) valorAbsoluto=-x; else valorAbsoluto=x;
(equivale a
valorAbsoluto= x<0 ? -x : x; )

Obter o maior valor entre x e y:

if(x>y) maior=x; else maior=y; /ou

if(x<=y) maior=y; else maior=x;

Comando escolha


Uma possível forma de generalização do comando se-então-senão  é o comando escolha. Podemos pensar no comando se-então-senão como sendo um comando que tem uma expressão e que tem um comando associado ao valor (diferente de zero) verdadeiro e um comando associado ao valor (zero) falso. Quando a expressão é avaliada será executado o comando associado ao valor resultante. No comando escolha temos uma expressão do tipo inteiro e temos vários comandos associados a rótulos que correspondem a constantes inteiras. O comando escolha na Linguagem C corresponde ao comando switch. O comando switch permite a transferência do fluxo de controle para um de vários pontos rotulados com uma constante inteira. A forma geral do comando é:
switch (<expressão>) {
  case <constante1>: <seqüência de comandos1>
  case <constante2>: <seqüência de comandos2>
  ...
  case <contanteN>: <seqüência de comandosN>
  default: <seqüência de comandos default>
}

Quando o fluxo de controle atinge o comando switch:

1.      a expressão inteira é avaliada

2.      o fluxo de controle passa para o comando rotulado com um valor correspondente ao resultado da expressão avaliada. Caso não exista uma constante inteira com rótulo correspondente ao valor da expressão inteira o fluxo de controle é desviado para o comando rotulado com a palavra chave default.

3.      o fluxo de controle passa a percorrer os comandos dentro do bloco e quando o controle atinge o final do bloco o fluxo de controle deixa o comando switch. O fluxo de controle deixa o comando switch quando não existe rótulo com o valor da expressão inteira e também sem o rótulo default.

 

A parte do comando switch entre abre e fecha chaves é conhecida como bloco do switch. O bloco do switch pode ter comandos que sejam prefixados com rótulos prefixados com a palavra chave case. Quando o fluxo de controle atinge o comando switch a <expressão> é avaliada e o fluxo de controle será desviado para a seqüência de comandos cuja constante do rótulo case tenha o mesmo valor da <expressão>. O fluxo de controle continua ao longo de todo os comandos a partir deste ponto, incluindo as outras seqüências de comandos. Se não existir um rótulo case com uma constante que pareie (faça par, seja igual) ao resultado da <expressão> o fluxo de controle será desviado para a seqüência de comandos com rótulo default. O rótulo default é opcional no bloco do switch e caso o resultado da <expressão> não tenha um rótulo case com contante que o pareie e não exista um rótulo default, todo o comando switch é saltado e o único resultado terá sido a avaliação da <expressão>.

Trecho de programa exemplificando um comando switch em C:
switch (a+b) {
  case 1: printf("1\n");
  case 2: printf("2\n");
  default: printf("outro\n");
}
Se o resultado de a+b for 1 serão impressas as três cadeias de caracteres: 1, 2,
outro. Se quisermos que cada seqüência de comandos de cada rótulo seja excutada de forma mutuamente exclusiva temos que usar o comando break. O comando break faz o fluxo de controle se dirigir para o comando seguinte ao switch. Veja o trecho de programa C abaixo:
switch (a+b) {
  case 1:
    printf("1\n");
    break;
  case 2:
    printf("2\n");
    break;
  default:
    printf("outro\n");
}
 

Comando enquanto e faça-enquanto


O comando enquanto é um dos denominados comandos iterativos ou comandos de repetição ou ainda simplesmente um laço. Os comandos iterativos prendem o fluxo de controle e permitem repetir a execução de comandos. Um comando enquanto tem associado a ele uma expressão e um comando ou seqüência de comandos. Quando o fluxo de controle atinge o enquanto a expressão é avaliada e caso ela seja (diferente de zero)verdadeira o fluxo de controle será transferido para o comando ou seqüência de comandos  associados ao enquanto. Ao terminar a execução do comando ou seqüência de comandos o fluxo de controle retorna para o inicio do comando enquanto, repetindo a avaliação da expressão e desviando o fluxo de controle para a execução do comando ou seqüência de comandos caso a expressão seja verdadeira. Esta repetição continua de forma indeterminada até que a avaliação da expressão dê resultado falso. Somente quando o resultado da expressão é falso é que o fluxo de controle deixa o comando enquanto e se dirige para o comando seguinte (seqüência).

A forma geral do enquanto (while) na linguagem C é :
while(<expressão>) <comando>;
Temos que usar bloco - {} - para associar uma seqüência de comandos ao comando while.
Uma forma alternativa do comando enquanto é denominada faça-enquanto (do-while):
do <comando> while(<expressão>);
No comando faça-enquanto o comando é executado pelo menos uma vez, no caso do comando enquanto se a expressão for falsa o comando não é executado nem uma vez.
 

Comando para


Um comando para tem associado a ele três expressões e um comando. O comando para é mais um dos comandos iterativos ou comandos de repetiçao. Considere a seguinte forma geral do comando para (for) na linguagem C:
for(<expressao1> ; <expressao2> ; <expressao3>) <comando>;
Podemos explicar a semântica deste comando utilizando o comando while. O comando acima tem o mesmo efeito do trecho de programa abaixo:
<expressao1>;
while (<expressao2>) {
  <comando>;
  <expressao3>;
}

A <expressao1> também é referida como iniciação; A <expressao2> será avaliada e se for 0  é falsa e se for diferente de 0 é verdadeira. A <expressao3> também é referida como atualização. Existem outras opções sintáticas para o comando for.

Exemplos:
for(i=0; i<20; i++) printf(“*”); //o código de iniciação tem uma atribuição e o código de atualização tem uma atribuição(incremento)

for(i=0, j=1; i!=j; ){ ... }; // o código de iniciação tem duas atribuições e o código de atualização esta ausente.

Um programa pode ficar com o fluxo de controle preso em um comando de iteração, o comando
while(TRUE);
Equivale ao comando
for(;;);
e corresponde a prender o fluxo de controle sem previsão explícita de saída do controle. Observe que os itens 1 2 e 3 do comando for são opcionais, e a ausência do <expressao2> faz o compilador interpretar o comando como sendo
for(;TRUE;);


O entendimento de um trecho de programa contendo comandos de iteração (fluxo de execução laçado!) não é uma tarefa simples. O âmago de várias linguagens de programação é substituir a iteração por outros tipos de construções. Existem várias maneiras de enxergar uma iteração sendo que algumas são aplicáveis em alguns casos e outras não. O chamado “desdobramento de um laço” muitas vezes ajuda no entendimento inicial na leitura de programas. Não vamos mostrar um tratamento sistemático deste assunto e ao invés disso vamos mostrar alguns exemplos simples.

 

Considere o seguinte trecho de programa:

for(i=0; i<3; i++) printf(“%d\n”, i);

este trecho pode ser “substituido” por este trecho:
printf(“%d\n”,0);

printf(“%d\n”,1);

printf(“%d\n”,2);

Agora considere este outro trecho:

scanf(“%d”, N);

for(i=0; i<N; i++) printf(“%d\n”, i);

este trecho pode ser substituído por estre trecho:

printf(“%d\n”, 0);

printf(“%d\n”, 1);

printf(“%d\n”,N-1);

Mas agora foi necessário usar “o poder das reticências”. Em algumas especificações de iteração torna-se ainda mais difícil o entendimento do significado, mas a técnica do “desdobramento” é uma importante ferramenta mental para entendimento dos laços. A execução repetida de comandos, além da iteração, pode ser feita, também, utilizando a “Recursão”, conforme será visto mais à frente na disciplina.


 

Escreva um programa em C que lê um valor inteiro N e desenha um quadrado feito com asteriscos de tamanho NxN

exemplo

para N=2
**
**

Escreva um programa em C que lê um valor inteiro N e desenha um triângulo feito com asteriscos de altura N.