Aulas de C

Aprendizado continuo. Linguagem antiga e moderna

Controle de Fluxo e Operandos Lógicos

Olá!
Vamos continuar então a programar em C.

Os dois primeiros programas, Hello World e o de Entrada de Dados, tinham como ponto comum o fato de serem, vamos dizer assim, diretos. Eles eram executados, realizavam cada instrução em sequencia e se encerravam.
Isso em si não é ruim: muitos programas simples e interessantes podem ser feitos assim. Porém, é perceptível que eles não possuem nenhuma “inteligência”: eles não conseguem executar instruções dependendo das entradas que lhe são oferecidas. Isso se deve ao fato de eles não possuírem nenhum controle de fluxo. Em programação, esse termo é adotado para definir situações onde o programa pode mudar a sequencia de execução do programa conforme condições estipuladas no momento do desenvolvimento da aplicação. Por exemplo: no programa de Entrada de Dados, poderíamos estipular valores limite para primeiro e segundo que não os limites do tipo int.
Pois bem, veremos agora um programa com um pouco mais de “inteligência”, pois iremos falar mais sobre os comandos de controle de fluxo em C e sobre os operadores lógicos, além de questões sobre o tipo booleano (ou melhor, sobre a ausência de um tipo booleano) em C.
No caso, vamos escrever um outro programa comum, o “adivinhe o número”. Mas vamos adicionar alguma “inteligência” a ele e tornar ele um pouco mais desafiador:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* Definindo o número limite para a pessoa tentar descobrir */

#define LIMITE 10
#define ENCERRA printf(“Pressione qualquer tecla para continuar!\n”); while(!getchar());

int main (int argc, char** argv)
{
  const int
limiteTentativas=(LIMITE/2)-1;
  int numeroPensado=0, numeroDoUsuario=0, maximoTentativas=0,i;
 
  srand(time(NULL)); // Serve para modificar a tabela de números pseudo-aleatórios
  numeroPensado=rand()%LIMITE+1;

  printf(“OK! Pensei em um número entre 1 e %d e te desafio a achar ele!\n”, LIMITE);
  do
    {
      printf(“Quantas tentativas você acha que precisa para descobrir ele? “);
      scanf(“%d”,&maximoTentativas);

      if (maximoTentativas<1)
         printf(“Você precisa tentar ao menos uma vez! :P\n”);

      if (maximoTentativas>limiteTentativas)
         printf(“%d tentativas? Tá querendo demais também! :P\n”,maximoTentativas);
    } while (maximoTentativas<1 || maximoTentativas>limiteTentativas);

  for (i=1; i<=maximoTentativas;i++)
    {
      printf(“Vamos lá! %da. tentativa! “,i);
     
      scanf(“%d”,&numeroDoUsuario);

      if (numeroDoUsuario==numeroPensado)
      {
         printf(“Parabéns! Você acertou!\n”);
         ENCERRA
         return(0);
      }
         else
         {
           printf(“Não pensei em %d. “, numeroDoUsuario);
           if (numeroDoUsuario<numeroPensado)
              printf(“Pensei em um número maior.\n”);
           if (numeroDoUsuario>numeroPensado)
              printf(“Pensei em um número menor.\n”);
         }
    }

    printf(“Que pena! Eu pensei em %d! Melhor sorte da próxima vez! \n”,numeroPensado);
    ENCERRA
    return(0);
}

Como de costume, iremos ignorar algumas coisas que já vimos anteriormente. Portanto, se você ficar com alguma dúvida, dê uma relida nos posts anteriores para fixar bem o conteúdo que já lidamos. No caso, vamos falar sobre os trechos de código que estão com destaque colorido no fonte àcima.

#define e os “números mágicos”

Logo de cara incluímos duas novas bibliotecas em C: stdlib.hSTanDard Library (biblioteca padrão), que contêm uma série de funções cotidianas em C – e time.h, que contem funções para lidar com tempo. Dessas, a stdlib.h em especial é muito importante, pois uma grande quantidade de comandos C muito usados está incluída nela. No caso, estamos muito interessados nas funções relacionadas à geração de números randômicos, mas existe muito mais nela e no futuro falaremos muito sobre essa biblioteca.
Logo após, vemos dois novos comandos do pré-processador (ou macros, como são chamados tecnicamente):

#define LIMITE 10
#define ENCERRA printf(“Pressione qualquer tecla para continuar!\n”); while(!getchar());

A macro #define permite ao programador definir um símbolo que o pré-processador irá substituir por uma informação qualquer no momento da compilação. No caso, definimos dois símbolos:

  • LIMITE – um número com valor 10;
  • ENCERRA – uma sequencia de comandos C;

No caso, você pode se perguntar sobre a utilidade de definir-se símbolos. Esse sistema de símbolos permite:

  1. Criar “comandos” simples (caso do ENCERRA);
  2. Criar situações de compilação condicional (um tópico mais avançado, veremos no futuro);
  3. Evitar os chamados “números mágicos”;

“Números mágicos?! Virou Hogwarts agora?”
Na verdade não. Em programação, chamamos de números mágicos determinados números ou expressões que são colocadas no código para realizar determinadas funções. No nosso caso, precisamos limitar o maior número no qual nosso programa irá “pensar”. Poderíamos simplesmente escrever ele no nosso programa de maneira direta, mas como esse número iria se repetir várias vezes em vários trechos de código aparentemente sem relação um com o outro, com certeza a leitura e análise de erros do programa (que em programação é chamada de depuração) seria complicada. Por isso chamamos tais números de números mágicos: uma vez que ele entra, temos dificuldades para entender o que eles fazem.
No caso, ao criarmos um símbolo (LIMITE), podemos usar ele no nosso código que o próprio compilador (através do pré-processador) irá substituir todas as entradas do símbolo LIMITE pelo valor desejado (no caso, 10). Isso torna o programa mais legível e mais simples de ser modificado.
O mesmo vale para ENCERRA. Com ENCERRA, criamos um “pseudo-comando”, um símbolo que será substituído por uma série de comandos C. No caso, o comando escrito em ENCERRA ajuda alguns usuários de IDEs (em especial no Windows) a visualizarem os resultados dos seus programas.
Existe mais sobre #define que ainda iremos ver, em especial na questão dos “pseudo-comandos”, mas esse básico deve ajudar a compreender melhor as coisas até termos uma oportunidade de nos aprofundarmos nele (em especial, quando falarmos no futuro de outras macros de pré-processador, quando revisitaremos #include e #define).

Variáveis Constantes:

Após as macros temos uma declaração de variáveis: logo de cara, temos uma que é um pouco diferente do que o que vimos quando falamos dos tipos de variáveis e como as declaramos:

  const int limiteTentativas=(LIMITE/2)-1;

A principal diferença está na palavra reservada const antes da declaração do tipo int da variável limiteTentativas. O que essa palavra faz é indicar ao compilador que essa variável na verdade é uma constante do tipo desejado, portanto não poderá mais ser modificada uma vez que seja inicializada, o que acontece em seguida. No caso, a variável constante limiteTentativas tem seu valor inicializado com o resultado de (LIMITE/2)-1, ou seja, na divisão do valor do nosso símbolo LIMITE por 2, menos 1 (no caso atual, o valor final é 4).
Você deve estar se perguntando: “usar um símbolo com #define não seria mais interessante?”
Bem, nesse caso não. Perceba que o limite de tentativas é determinado baseando-se no valor de LIMITE, e o #define não realiza per se contas ou qualquer processamento: tudo o que ele faz é dizer que (1) existe um símbolo e (2) qual seu valor. Se modificarmos o valor de LIMITE, teríamos que modificar o valor desse novo #define manualmente.
Você pode pensar então: “por que não tornar a fórmula um símbolo via #define?”. A ideia parece boa, mas ela cai em um problema: quando você substituir o símbolo, você obrigaria o sistema a repetir várias vezes uma determinada conta. Podemos pensar em desktops com gigas de poder de processamento e isso aparentemente ser uma boa ideia, mas pense em um sistema embarcado e você verá que uma situação como essa iria provocar redução na velocidade do programa.
Já com a variável constante, temos uma situação onde a variável não poderá ser modificada mas ainda assim será calculada apenas uma vez, apenas exigindo uma recompilação no caso de uma mudança de LIMITE (o que iria acontecer de qualquer maneira), pois ao compilar, LIMITE seria substituído pelo seu valor.
“Eu tenho que OBRIGATORIAMENTE inicializar uma variável constante no momento em que a declaro?”, você deve estar se perguntando. Na realidade, da mesma forma que ocorre com as variáveis comuns, as variáveis constantes PODEM ser inicializadas após a declaração. O que é impedido é que uma variável constante receba OUTROS VALORES após a inicialização. Como constantes são normalmente usadas para delimitar de alguma forma o comportamento do programa (no nosso caso, utilizaremos limiteTentativas para delimitar o máximo de tentativas que uma pessoa terá de adivinhar o número “pensado” pelo computador), é uma boa prática inicializá-la antes de qualquer entrada de dados.
Bem, acho que fechamos aqui a questão das variáveis constantes, à exceção de uma coisa: por que logo abaixo vem uma segunda linha de declarações int?
Quando você coloca várias declarações e/ou inicializações de variáveis em uma mesma linha, o compilador irá entender que todas elas tem o mesmo comportamento. Se colocássemos as int em questão junto com a declaração const int de limiteTentativas, essas variáveis também seriam declaradas constantes, o que não é o comportamento desejado. Nesse caso, a regra é clara: declarações de constantes devem ser separadas das declarações de variáveis comuns.
Bem, agora que vimos o bastante sobre constantes para entendermos o programa, sigamos em frente.

rand(), srand() e o gerador de números pseudo-aleatórios:

Vamos dar uma olhada nesse bloco de código:

srand(time(NULL)); // Serve para modificar a tabela de números pseudo-aleatórios
numeroPensado=rand()%LIMITE+1;

A primeira linha utiliza duas funções: a primeira é srand(), da stdlib.h, que recebe um long int como entrada. Essa função serve para modificar a tabela de números pseudo-aleatórios do sistema, usando o long int de entrada como parâmetro para essa modificação. No caso, usamos a função time(NULL), da biblioteca time.h, que devolve o horário do sistema atual em UNIX TimeStamp (o número de segundos passados desde as 0:00 do dia 01/01/1970 até o momento atual). Na verdade, ela retorna o epoch time (outra forma pela qual pode ser chamado o UNIX TimeStamp) para qualquer data passada de maneira correta (não entraremos nesse detalhe aqui).
Por que chamamos os números do sistema de pseudo-aleatórios?
Porque o computador não consegue, pela própria natureza lógica, gerar um padrão 100% aleatório. O que pode ser feito é utilizar-se algoritmos que gerem números, considerando-se a probabilidade de sua ocorrência em sequencia, que se aproximem da aleatoriedade e montar uma tabela com ela. O problema é que, ao se “pegar” um número nessa tabela é que, caso ela não seja “inicializada” de maneiras diferentes entre cada execução, o valor deixará de ser aleatório, ainda que continue sendo não-determinístico (ou seja, você não consegue, a partir das sequencias de números, determinar com precisão as fórmulas matemáticas que as geraram). Para evitar esse problema, utilizamos um comando, o srand(), que irá modificar a semente da tabela de números pseudo-aleatórios, o que irá garantir que, a cada execução, como o Timestamp será diferente, a semente da tabela será diferente e, por sua vez, a saída da mesma será diferente (se quiser saber mais sobre a idéia de geração de números pseudo-aleatórios, dê uma olhada nesse artigo da Wikipedia). De qualquer modo, sabemos que o srand() irá garantir que os números “aleatórios” tenham esse comportamento o mais próximo possível da realidade.
Na linha seguinte, utilizamos rand() para obter um número aleatório. Porém, esse comando não nos permite definir qual o valor máximo a ser obtido, normalmente obtendo qualquer valor entre 0 e uma definição chamada RAND_MAX (normalmente o limite de um unsigned int). A opção padrão é obter o resto da divisão inteira entre o maior número que desejamos e o valor retornado. Nesse caso, obtemos um número que vai de 0 ao limite estipulado menos 1. No nosso caso, queremos um número entre 1 e o LIMITE estipulado, por isso adicionamos 1 ao valor obtido (lembre-se da prioridade de execução da operação de resto em relação à soma).
Bem, acho que já falamos demais sobre rand(), até porque não tem como aprofundar nesse caso sem entrar uma teoria extremamente complexa e totalmente fora do nosso escopo atual (e muito provavelmente futuro). Tudo que é preciso saber é que precisaos usar srand() antes de rand() para garantir o comportamento aleatório e que precisamos utilizar a operação resto para “limitar” o valor de rand().

Laço de repetição condicional – do {…} while e operações relacionais e lógicas:

Em seguida, vemos que ele irá apresentar o desafio ao usuário e entrará no bloco de código abaixo:

  do
    {
      printf(“Quantas tentativas você acha que precisa para descobrir ele? “);
      scanf(“%d”,&maximoTentativas);

      if (maximoTentativas<1)
         printf(“Você precisa tentar ao menos uma vez! :P\n”);
      if (maximoTentativas>limiteTentativas)
         printf(“%d tentativas? Tá querendo demais também! :P\n”,maximoTentativas);
    } while (maximoTentativas<1 || maximoTentativas>limiteTentativas);

No nosso caso, nos focaremos nos comandos em ciano. Eles representam um tipo de controle de fluxo que chamamos de repetição (ou iteração, em alguns lugares) condicional. O nome pomposo apenas quer dizer que o laço em questão vai ser executado enquanto uma determinada condição for cumprida. No nosso caso, esse comando funciona assim:

  do
    {
       <…>
    } while (<condicao>);

O que fazemos aqui é indicar que, enquanto a condicao estipulada for verdadeira, o programa continuará no laço.

Mas como construir uma condição lógica? Para isso, C nos fornece os operadores lógicos, que comparam valores e retornam “verdadeiro” ou “falso” conforme a situação.
Aqui cabe um parenteses antes de entrarmos nos operadores: o C não possui um tipo booleano (ou seja, verdadeiro ou falso) específico. Para o C, um valor 0, NULL (que é definido por padrão como 0) ou “” (string vazia) como falso e qualquer outro valor como verdadeiro. Isso é importante pois existe um bug em C muito comum que iremos discutir adiante.
Bem, dito isso, os operadores lógicos em C padrão são: 

Operador  

>= 

<= 
== 
!=
&&
||
!
Ação  
Maior do que
Maior ou igual a
Menor do que 
Menor ou igual a 
Igual a 
Diferente de
E lógico
OU lógico
NÃO lógico

(fonte: curso de C da UFMG)
Os operadores lógicos são escritos de maneira similar aos matemáticos, sempre comparando um valor a outro. No caso, por exemplo, do comando acima, temos três expressões:

  • primeiro, uma comparação maximoTentativas<1;
  • depois, uma comparação maximoTentativas>limiteTentativas;
  • e por fim, uma operação lógica ou entre os dois;

Como isso é resolvido no C? Primeiro, o sistema irá analisar se o maximoTentativas<1. Caso seja, ele nem irá fazer a segunda comparação maximoTentativas>limiteTentativas, pois o fato de a comparação lógica ser OU vai fazer o programa aceitar como verdadeira a condição e continuará no laço (chama-se a isso short-circuit logical analysis – análise lógica de curto-circuito, e parte do princípio que uma vez que tenhamos garantido que uma condição é sabidamente verdadeira ou falsa, não há necessidade de continuar-se processando a lógica em questão). Caso contrário, ele irá tentar a segunda comparação, maximoTentativas>limiteTentativas, para verificar se ela é verdadeira ou falsa e, portanto, a condição o será.
Parece meio confuso, mas é só pensar com calma que você irá entender. Procure imaginar valores para maximoTentativas e pense em qual será a resposta (lembre-se: no caso atual, limiteTentativas é 4).
O que acontecerá se o valor não fizer nenhuma das duas condições estipuladas ser verdadeira? Simples: a expressão como um todo será falsa (0) e o laço do…while irá se encerrar normalmente, com o programa seguindo adiante. Ou seja, somente quanto o usuário entrar com um número máximo de tentativas maior que 1 e menor ou igual que o limite de tentativas estipulado (no caso, 4), o laço será interrompido.
Temos uns ifs engraçadinhos dentro desse laço, mas não falaremos sobre ele agora. Vamos então seguir em frente.
Para fecharmos esse tópico, existe uma versão do do…while chamada simplesmente while. Ela é representada assim:

while(<condicao>)
{
    <…>
}

e sua única diferença em relação ao seu “irmão” é que, caso ao chegar na entrada do laço a condição for satisfeita, o sistema sequer irá entrar no bloco de código em questão. O do…while irá executar ao menos uma vez, uma vez que a condição é testada no momento da saída do laço, diferentemente do while, que é executada quando da entrada no laço.

Iterações – o comando for:

Após nosso usuário ter determinado quantas tentativas ele quer ter para acertar o número mágico, vamos então dar a chance a ele.

  for (i=1; i<=maximoTentativas;i++)
    {
      printf(“Vamos lá! %da. tentativa! “,i);
     
      scanf(“%d”,&numeroDoUsuario);

      if (numeroDoUsuario==numeroPensado)
      {
         printf(“Parabéns! Você acertou!\n”);
         ENCERRA
         return(0);
      }
         else
         {
           printf(“Não pensei em %d. “, numeroDoUsuario);
           if (numeroDoUsuario<numeroPensado)
              printf(“Pensei em um número maior.\n”);
           if (numeroDoUsuario>numeroPensado)
              printf(“Pensei em um número menor.\n”);
         }
    }

Aqui temos um número delimitado de tentativas por máximo de tentativas. Desse modo, podemos utilizar um outro comando de laço, o for, que é um outro caso de laço de repetição ou iteração. Diferentemente do do…while, porém, o for é uma iteração não-condicional. Na verdade, ele é condicional, mas ele é mais usado em situações na qual se espera que ele se execute um determinado número de vezes (pode-se usar ele até como um while diferente, mas não é boa prática e não falaremos sobre isso aqui).
O for tem como estrutura a seguinte:
  for (<inicializacao>; <condicao>; <iteracao>)
    {
       <…>
    }

No caso, ele é um pouquinho complexo, portanto vamos dar uma olhada no seu comportamento:

  • no momento em que o programa chega no for, a primeira coisa que é feita é executar os comandos em <inicializacao>. No nosso caso, ele inicializa a variável i em 1. Essa inicialização pode ser feita como desejado, mas restringindo-se a um comando (ou bloco de código);
  • Em seguida, o bloco de código do for será executado;
  • Ao terminar de executar-se o bloco de código do for, ele executa o comando que está em <iteracao>. No nosso caso, utilizamos um operador matemático ++, que é utilizado em C para adicionar-se um ao valor da variável ao qual ela sucede. Na verdade ele se comporta de uma maneira mais complexa, mas para o momento basta entender que i++ seria o equivalente a i=i+1;
  • Depois de executar a <iteracao>, o sistema irá verificar se a <condicao> colocada é verdadeira. Caso o seja, ele irá interromper o laço for da mesma forma que o while, caso contrário ele entrará e executará uma nova iteração. No nosso caso, testamos se i ainda é menor ou igual ao número de limiteTentativas. Lembrando que C não possui tipo booleano e que basta que o valor seja 0 ou “” para ser considerado falso e, portanto, o laço for seja interrompido;

Bem, acho que isso deve ter deixado claro como o for funciona. O primeiro printf do programa deve deixar claro que você vai passando por várias iterações até acertar o “número mágico” “pensado” pelo nosso programa (o do rand() do início do programa). Mas como o programa saberá quando fomos bem-sucedidos?
Para isso existe o nosso próximo comando.

Execução condicional – o comando if:

OK… Paramos falando sobre como o nosso programa saberá que fomos bem sucedido. Veja que temos um scanf lendo o que o personagem entrou naquela tentativa. Precisamos de um comando para decidir fomos bem sucedidos ou não em acertar a descoberta do número “mágico”  “pensado” pelo programa. Quem cuida disso é comando if do bloco abaixo:

      if (numeroDoUsuario==numeroPensado)
      {
         printf(“Parabéns! Você acertou!\n”);
         ENCERRA
         return(0);
      }
      else
      {
         printf(“Não pensei em %d. “, numeroDoUsuario);
         if (numeroDoUsuario<numeroPensado)
            printf(“Pensei em um número maior.\n”);
         if (numeroDoUsuario>numeroPensado)
            printf(“Pensei em um número menor.\n”);
      }

O if é uma estrutura importante de programação, pois ele executa blocos de código caso a condição determinada seja verdadeira:

      if (<condicao>)
      {
        <…>
      }
      else if (<outra_condicao>)
      {
        <…>
      }
      else
      {
        <…>
      }

O if irá executar o bloco de código abaixo da instrução caso condicao seja verdadeira. Caso contrário ele pode:

  1. Testar outras condições. Para isso, utiliza-se else if, com uma nova condição e um bloco de código adequado;
  2. Executar um código para exceção. Para isso, utiliza-se else e o bloco de código adequado;
  3. Não fazer nada;
Agora, vamos tentar ler esse trecho de código. A primeira coisa que ele testa é se numeroDoUsuario==numeroPensado, ou seja, se o número que o usuário entrou é igual ao número “pensado” pelo sistema. Caso seja, ele indica que foi bem sucedido e encerra o mesmo (perceba que temos um return(0) dentro do if. Isso é permitido pelo C). Perceba que o operador de relação de igualdade é ==. NUNCA CONFUNDA COM O OPERADOR DE ATRIBUIÇÃO, =. Esse é um bug (falha de programação) muito comum que é provocado por falta de atenção na programação. No caso de colocar =, o valor de numeroDoUsuario seria substituído pelo de numeroPensado. A não ser que numeroPensado fosse 0 (o que é impossível, devido à lógica do programa) o programa acusaria verdadeiro independentemente do valor realmente ser (lembre-se, qualquer valor diferente de 0 ou “” ou NULL é considerado pelo C como verdadeiro em relações lógicas).
Bem, dito isso, vamos continuar a analisar nosso programa. Caso a condição acima não seja verdadeira, ele irá ignorar o bloco abaixo de if e irá ver que temos um else com um bloco de código. Ele então entrará nesse bloco de código. A primeira coisa que ele irá fazer é dizer que não pensou no numeroDoUsuario e testar se numeroDoUsuario<numeroPensado. Caso seja verdadeiro, ele irá imprimir na tela uma mensagem dizendo que pensou em um número maior que o que o usuário entrou. Caso contrário, irá verificar se numeroDoUsuario>numeroPensado. Se verdadeiro (provavelmente será, se tudo mais deu errado), irá imprimir que pensou em um número menor que o que o usuário pensou.
Os ifs dessas linhas possuem uma característica interessante que é o fato de terem comandos diretamente abaixo deles, e não dentro de blocos de código. Na realidade isso deve-se ao fato que if, while, for e afins, todos eles possuem uma característica de executar na realidade apenas um comando. A diferença é que, para o compilador C, um bloco de código (lembre-se: blocos de código são comandos isolados pelos colchetes {}) são considerados comandos isolados. Portanto, um bloco de código == um comando.
Lendo com calma o código você irá compreender muito bem o mesmo. Leia e releia o códigoo e faça exercícios mentais para entender o código do if.

Quando tudo dá errado – considerações:

OK… Mas e caso o usuário não acerte mesmo depois de ter tentado o número de vezes que ele desejou (maximoTentativas).
Isso ocorre quando a variável i, após a iteração do for, ficar com um valor maior que maximoTentativas, tornando a condição  i<=maximoTentativas falsa e saindo do laço for (ao atingir o limite de iterações estipulado). Para encerrar o programa, nós fazemos o sistema imprimir uma mensagem dizendo qual o número no qual ele pensou e saindo do mesmo.
Antes de irmos para as “brincadeiras” finais, algumas considerações:

  • Os operadores relacionais que mostramos anteriormente só servem para valores numéricos ou para o tipo char. Em especial para strings, a biblioteca string.h traz funções que nos permite testar igualdade entre textos. Veremos eles melhor no futuro;
  • Ainda não esgotamos o assunto controle de fluxo. Em especial falaremos ainda sobre um tipo de execução condicional para múltiplos valores e sobre como “quebrar” laços como o for e o while;
  • É possível aninhar ifs ccom múltiplos else if e afins. Porém, tome cuidado ao usá-los: além de tornar o código de difícil leitura, você pode ter problemas dependendo do compilador (normalmente o C exige no mínimo 8 “níveis” de ifs aninhados, mas esse valor não é obrigatório);

Bem, dito isso, vamos fazer algumas sugestões para “brincadeiras” com o nosso código:

  • Comente ou remova o srand() e veja o comportamento do gerador de números pseudo-aleatórios;
  • Tente reescrever o código para tornar maximoTentativas uma constante. Lembre-se que não é necessário inicializar o valor imediatamente, podendo ser inicializado em um momento futuro;
  • No caso de quando o usuário erra o número “pensado”, você pode ter percebido que os dois ifs são redundantes. Tente reescrever o código usando apenas um if;
  • Da mesma forma que existe o operador ++, existe o operador , que subtrai um da variável que o antecede. Considerando isso e o funcionamento do laço for, tente reconstruir o laço para não ter uma condição no sentido exato da palavra. Dica: lembre-se que C trata 0 como falso;
  • Para ter uma idéia do problema que pode ocorrer quando se confunde o operador de igualdade com o de atribuição, modifique o código removendo um dos sinais de igual de if (numeroDoUsuario==numeroPensado) e veja o que acontece com o programa. Para maior clareza, coloque um printf que apresente o número “pensado” antes do usuário entrar o seu número e digite outro completamente diferente. Coloque também um printf mostrando o numeroDoUsuario após o bug;
  • Para entender a questão das definições, altere o valor de LIMITE e veja como o programa se comporta. Como dica, o cálculo do limiteTentativas baseia-se na idéia de buscar-se o número sempre indo na metade do que é válido. Por exemplo: computador escolhe 6. Na primeira interação, tento 5, e ele me fala que pensou um maior. Tento 7 (metade do bloco “acima de cinco”) e ele me diz que pensou um menor. Tento 6 e acerto;

Bem, semana que vem iremos reeforçar a teoria dos operadores (lista completa e exemplos) e daremos uma terminada na questão dos controles de fluxo. Até lá, divirtam-se!

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: