Stoa :: C/C++ :: Blog :: Histórico

Setembro 2007

Setembro 13, 2007

user icon

"O laço for" - Aspectos básicos - parte 1

 

O que é a instrução for?

A instrução for (que, em português, poderia ser traduzida como a instrução para) é uma instrução de controle de fluxo. É também comumente dito que a instrução for é uma instrução de loop (que é comumente traduzido em português como laço). A sintaxe da instrução for é:

 

 

for ( instrução de inicialização; condição de término; instrução de incremento ) { }

 

 

A instrução for serve para executar as instruções dentro do bloco um número controlado de vezes, sendo que o controle é feito pela condição de término. Do ponto de vista algorítmico, usa-se um laço for quando se precisa realizar um operação um número determinado de vezes, especialmente se a cada vez que essa operação é realizada é necessário utilizar um valor numérico que aumenta ou diminui com taxa de variação constante.

 

 

Mas o que é o fluxo de um programa?

O fluxo é o "caminho" que o computador irá percorrer através de um programa em C/C++, isto é, o fluxo é a sequência das instruções de um programa. Dentro da conhecida metáfora da programação como culinária, o fluxo é equivalente à idéia de que, ao preparar uma receita, a ordem em que os ingredientes são adicionados geralmente tem grande importância (se não me engano, isso é especialmente verdadeiro ao se fazer bolos).

 

 

O que quer dizer "for é uma instrução de controle de fluxo"?

Afirmar que a instrução for é uma instrução de controle de fluxo significa dizer que a instrução for controla a sequência de instruções a serem executadas pelo computador. Metaforicamente, a instrução for é como o procedimento da receita que diz: "adicione um ovo; bata até ficar uma massa homogênea; repita esta operação três vezes". Esta parte do procedimento, "repita", faz com que o cozinheiro tenha que retornar a um ponto anterior da receita e executar novamente os procedimentos já feitos uma vez, ou seja, o cozinheiro adiciona um ovo e bate até a massa ficar homogênea; lê que deve fazer isso três vezes, e só fez uma, portanto, retorna ao procedimento de adicionar um ovo e bater até a massa ficar homogênea; lê que deve fazer isso três vezes, já fez duas, e portanto retorna ao procedimento de adicionar um ovo e bater até que a massa fique homogênea; aí lê que deve fazer isso três vezes, e como já fez três vezes, passa para o procedimento seguinte da receita (que deve ser algo como "ponha numa forma untada").

 

 

O que é uma instrução de loop?

Loop é a palavra em inglês para laço, no sentido de "dar uma volta num círculo". Portanto, percorrer um laço é percorrer um caminho que termina no ponto de início. Dizer que a instrução for é uma instrução de laço significa que a instrução for corresponde a um círculo ou a um trajeto que o computador irá percorrer mais de uma vez, retornando ao início da trajetória cada vez que percorrer essa trajetória "até o fim". Mas, tal como a expressão controle de fluxo indica, este laço é percorrido de maneira controlada - ou seja, a trajetória é percorrida tantas vezes quanto necessário, nem mais nem menos - ou pelo menos deveria ser assim. Na verdade, com a instrução for é possível construir um programa em C/C++ que tem execução infinita - basta instruir o computador a executar um procedimento um número de vezes tão grande que seria necessário um tempo muito grande para que o a instrução for seja terminada - digamos, um milhão de anos. Mas também seria possível criar um programa com uma instrução for que tem uma condição de controle de fluxo que nunca é satisfeita - de modo que a instrução for é executada para sempre, não terminando nem mesmo em um milhão de anos.

 

 

Como é a sintaxe da instrução for?

A sintaxe da instrução for é:

 

 

for ( instrução de inicialização; condição de término; instrução de incremento ) { }

 

 

A instrução de inicialização é uma instrução que será executada antes que a primeira execução das instruções dentro do bloco do laço for seja executada - em outras palavras, o bloco (as instruções dentro das chaves) do laço for será executado tantas vezes quanto o controle de fluxo determinar; mas, antes que esse bloco seja executado pela primeira vez, e antes mesmo que a condição de controle de fluxo seja verificada, a instrução de inicialização é executada.

 

 

A condição de término é uma expressão que, se for verdadeira, fará com que o laço for seja executado novamente; se a condição_de_término for falsa, o laço for será encerrado. É verdade que isso é um tanto anti-intuitivo, pois seria mais razoável que a condição de término verdadeira encerrasse o laço, mas isso se dá apenas devido ao nome incorreto dessa expressão, que o uso consagrou como sendo condição de término mas que deveria ser na verdade condição de execução. Nomeada dessa forma, a condição verdadeira faz com que o laço seja executado; se falsa, o laço é encerrado.

 

O bloco de instruções costuma-se dizer que está "dentro" do laço for. De modo que se algum colega seu mais adiantado no estudo do misticismo* disser que seu programa tem "algo errado dentro do segundo for" isso quer dizer que ele acredita que há um erro nas instruções contidas no bloco a ser executado controladamente pelo segundo laço for do seu programa.

 

 

*misticismo = informática.

 

A instrução de incremento é uma instrução que é executada uma vez para cada vez que o computador percorre o laço for. Ela é executada após a execução do bloco de instruções e antes da verificação da condição de término. A instrução de incremento, tal como poderá ser visto na segunda parte deste texto, pode ser qualquer instrução de C ou C++, mas geralmente se trata de uma atribuição de valor a variável, e muito comumente uma adição ou subtração de uma unidade. 

 

Palavras-chave: controle de fluxo, for, laço, sintaxe

Postado por Renato Callado Borges em C/C++ | 0 comentário

Setembro 17, 2007

user icon

Descrição: quando o computador encontrar uma instrução for tal como a descrita a seguir, ele irá realizar uma certa sequência de operações, descritas logo abaixo do código.

 

int a;

for ( a = 0; a <= 10; a = a + 1 )

{

   int b;

   printf("\nDigite um valor para b: ");

   scanf("%d", &b);

   printf("\na = %d, b = %d e sua soma = %d.", a, b, a + b);

}

printf("\nA variavel a ainda existe e vale %d.", a);

 

 

A declaração da variável a ocorre antes do laço for. Isto significa que o escopo no qual a está definida abrange todo o laço for considerado.

Assim que o computador encontra a instrução for, ele executa a instrução de inicialização. Neste caso, ele atribui o valor zero à variável a. Note que a execução dessa instrução de inicialização ocorre antes de executar qualquer instrução "dentro" do laço e antes mesmo do teste da condição de término. Em outras palavras, quando um laço for é executado, sua instrução de inicialização é sempre executada.

A segunda operação realizada pelo computador ao percorrer uma instrução for é verificar se a condição de término está satisfeita. Caso essa condição seja falsa, o laço for é encerrado e o computador executará a próxima instrução do programa, que no caso do exemplo seria a impressão da variável a fora do laço for. Mas como no exemplo, na primeira execução a variável a tem valor zero e o teste condiciona a execução do bloco de instruções aos casos em que a é menor ou igual a 10, o bloco será executado.

A execução do bloco segue as regras gerais de C ou C++ para a execução de instruções. Note que "dentro" de um laço for podemos ter outras estruturas de controle de fluxo, como laços for, while, do ... while, switch e if. Com esse recurso, podemos ter um bloco de código que é executado condicional e/ou controladamente dentro de um bloco que já é ele mesmo executado controladamente. Com esse recurso podemos escrever código para problemas que exigem que operações sejam repetidas repetidas vezes, como por exemplo somar vários fatoriais. (Cada fatorial repete a multiplicação de 1 a N, e cada soma soma os fatoriais de 1 a M, por exemplo).

No nosso exemplo, o bloco de instruções cria a variável b e pede que o usuário entre com o valor para b. Depois ele calcula e imprime o valor de a vezes b. Ao chegar no final do bloco - e isto é muito importante - a variável b é destruída, pois o bloco é encerrado. A variável a, como tem um escopo mais amplo que o do laço for, continua a existir.

A última etapa que o computador empreende ao executar uma instrução for é executar a instrução de incremento. No nosso exemplo, a variável a é aumentada em uma unidade. Em seguida o computador retorna à etapa de verificar se a condição de término é satisfeita - note que a instrução de inicialização não é mais executada. Se o teste for verdadeiro, como no nosso caso, o bloco é executado novamente, será destruído e a variável a incrementada novamente (a = 3, agora). Como o teste é verdadeiro, o bloco é executado e então destruído. a é novamente incrementada (a = 4). Esse ciclo ocorre até que a seja maior que 10, e aí o bloco não é executado e o processamento segue seu caminho "para fora" do laço for.

 

 

Exemplo: digamos que seu problema seja somar os números naturais de 1 a 1000. A solução seria criar e inicializar uma variável inteira (digamos a variável "soma") com valor zero, e depois somar a essa variável o valor 1 (soma = 1), depois somar a essa variável o valor 2 (soma = 3), depois somar a essa variável o valor 3 (soma = 6) e assim sucessivamente até mil.

Esse procedimento para obter a soma dos 1000 primeiros naturais consiste em uma operação, "adicione o valor do i-ésimo natural à variável soma", que deve ser repetida de i=1 até i=1000, aumentando i em uma unidade a cada execução. De modo que o laço for que implementa essa solução para esse problema seria (em C):

 

 

/* Soma os 1000 primeiros naturais e armazena em "soma" */

int a, soma = 0;

for ( a=1; a <= 1000; a++ )      /* Lembre-se: "a++" é o mesmo que "a = a + 1". */

   soma += a;

 

 

Exercício: escreva um laço for que calcula o fatorial de 10. (Lembre-se: dez fatorial = 10! = 10*9*8*7*6*5*4*3*2*1 = ?).

 

 

Exemplo: talvez seu problema seja somar os números naturais pares de 1 a 1000. Neste caso, a solução é mudar a instrução de incremento para que aumente a variável de controle em duas unidades a cada execução, e garantir que a instrução de inicialização inicializa a variável com o valor par correto. Em código:

 

 

/* Soma os números naturais pares de 1 a 1000 */

int a, soma = 0;

for ( a=2; a <= 1000; a += 2)     /* Lembre-se que "a += 2" é o mesmo que "a = a + 2". */

   soma += a;

 

 

Exercício: escreva um laço for que soma os números ímpares de 1 a 1000.

 

 

Desafio: escreva um laço for que conta o número de anos bissextos que ocorreram no século XX. Modifique esse laço para que conte os anos bissextos de qualquer século da era atual.

Palavras-chave: exemplos, exercícios, for, laço for

Postado por Renato Callado Borges em C/C++ | 0 comentário

Setembro 26, 2007

user icon

Detalhe: Em C, existe a restrição de que a instrução de inicialização não pode ser uma declaração de variável. Em C++ isso é permitido.  De modo que a instrução

 

for (int a=0; a < 10; a++) { }

 

 

está errada em C, mas é correta em C++.

 

 

Detalhe: A instrução de inicialização na verdade pode ser composta de mais de uma instrução, desde que as instruções sejam separadas por vírgulas. Por exemplo:

 

 

for (a=0, b=34, c=67; a < 100; a++) { /* Instruções a serem executadas a cada interação */ }

 

 

é um laço for perfeitamente válido. Existem algumas restrições, por exemplo eu testei e no compilador gcc 4.1.2, em C não é permitido criar um laço for como uma instrução de inicialização, mas não sei dizer com certeza qual é o critério que diz que um certo tipo de instrução é permitido e qual não. Mas tendo em vista que se trata de um recurso da linguagem que pode facilmente tornar o código menos legível, recomendo aos programadores que se restrinjam a utilizar as instruções múltiplas na instrução de inicialização apenas quando se tratarem de instruções de atribuição de valor a variáveis, tal como ilustrado no exemplo acima.

 

 

Detalhe: assim como a instrução de inicialização, a condição de término pode ter mais de uma expressão, desde que separada por vírgulas. Entretanto, o valor de verdadeiro ou falso será dado exclusivamente pela última expressão, e portanto essas expressões "extras" não fazem sentido a menos que se saiba do detalhe seguinte.

 

 

Detalhe: a condição de término não precisa ser uma expressão, podendo ser também uma instrução. Neste caso, uma atribuição recebe valor falso caso o valor atribuído seja igual a zero, e verdadeiro em qualquer outra situação. No caso de uma chamada a função, se esta função for do tipo void haverá um erro, e caso ela seja numérica o valor será falso para zero e verdadeiro para qualquer outro valor. Caso a função retorne um tipo definido pelo usuário ocorrerá erro. Caso a condição de término seja uma variável, o valor será falso para zero e verdadeiro para qualquer outro valor.

 

 

Exemplo: Considerando estes dois detalhes, a seguinte instrução (que, note bem, é pouco legível) é possível em C++:

 

 

for (int a=0, int b=10; a*=b, b; b--); /* Você sabe dizer o que esta instrução calcula? */

 

 

Detalhe: qualquer uma das expressões dentro dos parênteses do laço for pode ser omitida. A omissão de cada parte terá efeitos diferentes: um laço for sem instrução de inicialização apenas não executa nada antes do percurso normal do laço. Um laço for sem condição de término é executado infinitamente, de modo que para que o programa tenha fim ele terá que ter dentro do laço for ao menos uma das seguintes instruções: break, return, goto ou uma chamada a função que execute uma dessas instruções implicitamente. Uma função sem instrução de incremento não executa nada ao final da execução do bloco e antes do teste da condicional, de modo que a modificação da variável testada deve ser feita dentro do corpo do laço for, ou por meio de algum recurso externo, como uma chamada a função que possua variável static. Observe que apesar de pdoerem ser omitidos todos os itens da instrução for, os pontos-e-vírgula devem comparecer obrigatoriamente.

 

 

Exemplo: a instrução seguinte cria um programa infinito! Se você deixar ser computador rodando esse programa, ele só irá parar quando acabar a luz!

 

for ( ; ; );

 

 

Exemplo: o for abaixo é ligeiramente diferente: ele é a maneira de se escrever com um for algo que seria mais razoável escrever usando while. Mas essa é uma característica importante dos controles de fluxo: praticamente tudo o que pode ser feito com uma estrutura pode ser feito com as outras. Somente uns poucos casos excepcionais exigem uma solução ou outra, especificamente; a maioria dos problemas que pode ser resolvidos com um for pode ser resolvida com um while e vice-versa.

 

 

/* Fazer alguma coisa repetidamente até que o usuário entre com o valor zero */

int a;

for ( ; ; ) {

   scanf("%d", &a);

   if ( a == 0 )

       break;

   /* Faz alguma coisa */

}

 

Exercício: escreva um programa que calcula a soma dos fatoriais dos primeiros números naturais até que essa soma seja maior que mil.

 

 

Desafio: escreva um laço for que calcula a soma dos quadrados dos 20 primeiros números naturais. Reescreva o laço sem usar nenhuma instrução no bloco.

Palavras-chave: for, laço for

Postado por Renato Callado Borges em C/C++ | 0 comentário