sábado, 26 de dezembro de 2009

Um vetor especial... Final (string/funções)

Como já disse o objetivo do C em tratar o vetor de caracteres (string) de uma forma especial é para facilitar nossa vida. Vamos ver isto hoje.

Funções especiais para um vetor especial.
Existem uma série de funções para trabalhar com strings em C que, em sua maioria, estão na biblioteca string.h.
Vamos conhecer as mais comuns e as facilidades que elas trazem.

Obtendo uma string (gets)
Até agora usamos a função scanf para obter dados inseridos pelo usuário.
Poderíamos continuar usando scanf para strings também, desta forma abaixo:

 char nome[21];
 scanf("%s",&nome);

Primeiro declaramos um vetor de caracteres chamado nome capaz de armazenar uma string de até 20 caracteres. Depois usamos a função scanf para armazenar neste vetor os caracteres digitados pelo usuário. Observe o uso do especificador de tipo "%s", que se refere a string.
Entretando o C possui uma função especializada para esta obtenção dos dados digitados pelo usuário: gets. Olhe o seu uso abaixo:

 char nome[21];
 gets(nome);

Bem menos digitação, o que significa uma economia de dedos (rsrs).
Ao contrário do que você possa pensar, a função gets está na biblioteca stdio.h e não na string.h.

Verificando o tamanho de uma string (strlen)
Quando definimos um vetor nem sempre sabemos se ele será totalmente preenchido. Na maioria dos casos, definimos um tamanho considerado razoável, que pode ser muito maior que o tamanho que será efetivamente usado. No exemplo acima utilizamos um vetor capaz de armazenar 20 caracteres. Se o usuário digitar um nome como ANA, por exemplo, que tem 3 caracteres nosso programa 'achará' que os 20 caracteres foram preenchidos. Poderíamos achar o tamanho efetivamente ocupado no nosso vetor fazendo um for e incrementando uma variável contadora até que o caracter \0 fosse encontrado.
No entanto o C facilita a nossa vida com uma função própria para isto a strlen. Observe o seu uso abaixo:

 int tamanho;
 tamanho = strlen(nome);

Ela retorna um inteiro com o número de caracteres efetivamente ocupado no nosso vetor.

Comparando strings (strcmp)
Imagine que você tenha que comparar 2 vetores. Seria necessário, avaliar cada item de um vetor e compará-lo com o respectivo item do outro vetor. Além disso, os vetores podem ter tamanhos diferentes.
No caso de strings, poderíamos verificar o tamanho de cada uma delas e, se fossem do mesmo tamanho, comparar os seus caracteres individualmente. Seria uma solução.
Mas não precisamos fazer isto. Temos uma função que faz isso para nós: strcmp. Observe o seu uso:

 char outro_nome[21];
 int result;
 result = strcmp(nome, outro_nome);

Esta função devolve um valor inteiro que corresponde ao resultado da comparação entre as duas strings da seguinte forma:

- result será menor que 0 (ou seja, um inteiro negativo) se nome for diferente e/ou menor que outro_nome;
- result será 0 se, e somente se, nome for igual a outro_nome (entenda por igual o mesmo número de caracteres, e os mesmo caracteres (um 'A' é diferente de um 'a') na mesma posição relativa (índice) em cada vetor;
- result será maior que 0 (ou seja, um inteiro positivo) se nome for diferente e/ou maior que outro_nome.

Em sua forma mais comum, só é verificado o resultado diferente de ou igual a 0.

Copiando uma string em outra (strcpy)
Para copiar o conteúdo de um vetor para outro vetor é necessário copiar item a item, e ainda verificar se o tamanho dos 2 vetores é compatível.
Seria possível copiar um vetor de 20 inteiros em um vetor de 10?
No caso das strings, temos uma função para fazer esta cópia: strcpy. Observe o seu uso:

 strcpy(nome, outro_nome);

Aqui estamos copiando o conteúdo de outro_nome em nome. Preste bastante atenção a isto, porque é muito fácil confundir.
Um detalhe importante é que alguns compiladores(acho que a maioria) permite copiar uma string maior em uma menor sem perda de dados. Na verdade a string menor é aumentada. Como isto é possível? Vejamos a próxima função.

Juntando strings (strcat)
Como juntar 2 vetores afim de formar 1 único vetor?
Poderíamos criar um 3o vetor cujo tamanho fosse igual a soma dos 2, e copiar todos os itens de cada um dos 2 vetores.
No caso de strings, temos uma função que faz isto para nós: strcat. Observe o seu uso:

 strcat(nome, outro_nome);

Ela concatena (que palavra estranha rsrs; prefiro junta ou acrescenta) outro_nome no final de nome. O tamanho de nome será a soma do tamanho anterior de nome com o tamanho de outro_nome.


Existem outras funções especializadas para o trabalho com strings. A medida que elas forem necessárias para a solução de algum problema proposto, nós falaremos sobre elas.
Para encerrar, vou deixar um pequeno código para exemplificar as funções que vimos neste post.


#include "stdio.h"
#include "stdlib.h"
#include "string.h"
 
int main(){
 char nome[21], outro_nome[21];
 char igualdade[10], frase[16] = "As strings sao ";
 int tam_nome, tam_outro;
 
 printf("Digite um nome (no maximo 20 caracteres): ");
 gets(nome);
 tam_nome = strlen(nome);
 printf("Digite um outro nome (no maximo 20 caracteres): ");
 gets(outro_nome);
 tam_outro = strlen(outro_nome);
 if (strcmp(nome, outro_nome) == 0)
    strcpy(igualdade,"iguais");
 else
    strcpy(igualdade,"diferentes");
 printf("%s tem %d caracteres e %s tem %d caracteres\n", nome, tam_nome, outro_nome, tam_outro);
 strcat(frase, igualdade);
 printf("%s\n", frase);
 printf("Se juntar %s e %s ", nome, outro_nome);
 strcat(nome, outro_nome);
 printf("o novo tamanho de %s sera %d\n", nome, strlen(nome));
 system("pause");
}


Viu como é fácil?

quinta-feira, 24 de dezembro de 2009

Um vetor especial... Parte I (string/conceitos)

Hoje vamos falar sobre um vetor que o C trata de forma especial: o vetor de caracteres, também chamado de string.

A comunicação entre o homem e a máquina!
Como já aprendemos aqui, um vetor é um agrupamento de variáveis do mesmo tipo. Até agora trabalhamos com vetores de inteiros. Apesar de termos usado vetores de caracteres desde este post.
Você talvez não tenha percebido ainda, mas tudo o que está digitado aqui são vetores de caracteres. Uma palavra é formada de letras (caracteres) agrupadas (vetor).
Este é o tipo de vetor mais utilizado em programas que interagem com o usuário (mesmo nos programas com interface gráfica).
Por ser muito usado, este vetor é tratado de forma especial pelo C. Não que ele seja mais complicado, mas sim para facilitar o trabalho dos programadores (no caso, nós).
Vejamos como isto é feito.

Não acaba onde termina!
Como já falamos, um vetor em C sempre começa com o índice 0 (zero), mas não possui nenhum restrição quanto ao seu tamanho, ficando sob a responsabilidade do programador.
Relembrando: Se declararmos um vetor de inteiros com o tamanho 5 (int vet[5]), o seu último elemento será o que tem o índice 4 (tamanho do vetor - 1), mas se fizermos uma atribuição usando o índice 5 (vet[5] = 10), ela será aceita (apesar de estar errada). O compilador não tem como saber onde termina este vetor.
No caso do vetor de caracteres é necessário indicar este fim para que o compilador saiba onde ele acaba. Isto é feito com um caracter especial (\0).
Observe a figura abaixo:



Esta é a forma como o C armazena a palavra CASA. Um vetor de caracteres terminado por \0.
A forma de declarar e inicializar este vetor é:

  char vet[5] = {'C', 'A', 'S', 'A'};

ou como é mais comum:

  char vet[5] = "CASA";

Acredito que esta 2a forma é familiar. É ela que temos usado com a função printf. Observe que a palavra CASA tem 4 letras, mas nosso vetor tem tamanho 5, ou seja, existe um caracter a mais para guardar o \0.
No próximo post veremos como esta forma especial que o C trata os vetores de caracteres facilita nossa vida.

Aproveitando o post, gostaria de desejar a todos um ótimo Natal, com saúde, amor e paz, que são as coisas que realmente importam. Um abraço a todos.

quinta-feira, 17 de dezembro de 2009

Vetores x Funções

Recebi um comentário (neste post) com uma pergunta. Gostaria de agradecer o comentário e incentivar outras pessoas a fazer o mesmo. TEM ALGUMA DÚVIDA SOBRE O QUE ASSUNTO DO POST: PERGUNTE NOS COMENTÁRIOS!
Como o objetivo do blog é tornar o C mais fácil, sempre que receber alguma dúvida, responderei, no próprio comentário, quando for simples, ou num novo post, quando esta resposta necessitar de uma atenção maior (como é o caso hoje). Claro que observando a sequência dos assuntos (não venha me perguntar sobre árvores balenceadas ainda, na hora certa vou falar sobre elas).
A pergunta é esta: "Como faço para criar uma função que preencha e some vetores?
Ou não tem como fazer isso com função?"
A resposta é SIM. Antes de mostrar como, vamos ver (e rever) um simples detalhe.

No post em que falamos sobre ponteiros, aqui, vimos que o nome de um vetor corresponde ao seu endereço. Isto significa que um vetor sempre estará acessível a qualquer parte do programa (se conheço o endereço, posso acessar), bastando saber o seu nome. Assim, um vetor SEMPRE é passado às funções por referência, não tendo, nem ao menos, a necessidade de usar o & antes do nome do vetor.
Visto isto, vamos ao como.


//SOMANDO VETORES USANDO FUNÇÕES
#include "stdio.h"
#include "stdlib.h"
 
const int TAM=5;
 
int ler_int(){
 int digitado;
 
 printf("Digite um valor inteiro: ");
 scanf("%d",&digitado);
 return digitado;
}
 
void mostra_int(int num){
 printf("%d ",num);
}
 
void preenche_vetor(int vet[]){
 int i;
 
 for(i = 0; i < TAM; i++)
    vet[i] = ler_int();
}
 
void soma_vetores(int vet1[], int vet2[], int vet3[]){
 int i;
 
 for(i = 0; i < TAM; i++)
    vet3[i] = vet1[i] + vet2[i];
}
 
void exibe_vetor(int vet[]){
 int i;
 
 printf("\n");
 for(i = 0; i < TAM; i++)
    mostra_int(vet[i]);
 printf("\n\n");
}
 
int main(){
 int vetor_A[TAM], vetor_B[TAM], vetor_C[TAM];
 
 printf("Preenchendo o 1o vetor\n");
 preenche_vetor(vetor_A);
 exibe_vetor(vetor_A);
 printf("Preenchendo o 2o vetor\n");
 preenche_vetor(vetor_B);
 exibe_vetor(vetor_B);
 printf("Somando os vetores...\n");
 soma_vetores(vetor_A, vetor_B, vetor_C);
 printf("\n\n");
 printf("Exibindo o vetor resultante da soma\n");
 exibe_vetor(vetor_C);
 system("pause");
}


O código acima soma o conteúdo de 2 vetores (vetor_A e vetor_B) num 3o vetor (vetor_C).
Na linha 5 definimos e inicializamos uma constante inteira TAM que armazena o tamanho dos nossos vetores. Não é obrigatório, mas é uma excelente prática de programação. Por quê?
Neste exemplo trabalhamos com vetores de 5 elementos. Se decidirmos aumentar o tamanho desses vetores para 10 elementos, só precisaremos alterar uma única linha de código. Se não tivéssemos usado a constante precisaríamos alterar 4.
Uma novidade está na linha 19, é a forma de especificar um vetor como um parâmetro de uma função. A sintaxe formal é esta:

 <tipo de retorno> nome da função (<tipo de dados do vetor> nome do vetor [])

Note que não é necessário informar o tamanho do vetor.
Poderíamos inclusive usar a seguinte sintaxe (que é muito comum em programas profissionais):

 <tipo de retorno> nome da função (<tipo de dados do vetor>* nome do vetor)

O nome de um vetor corresponde ao seu endereço na memória, portanto, podemos tratá-lo como um ponteiro. Pode fazer um teste se quiser.
Já quando chamamos a função, linha 46, não é necessário o uso dos [].
Um erro muito comum é colocar os [] com o tamanho do vetor dentro deles. Ao fazer isto, ao invés de se passar o vetor inteiro, está sendo passada à função um único elemento do vetor, e pior ainda, um elemento que nem faz parte do vetor, já que por iniciar em 0(zero), o último elemento de um vetor é igual a tamanho do vetor - 1.
O restante de código já é (acredito ;)conhecido.

Mais alguma dúvida?

(Para pensar: Uma função pode retornar um vetor?)

terça-feira, 15 de dezembro de 2009

Dividir para conquistar! Final (valor x referência)

Encerrando a nossa série sobre modularidade (existem outras coisas a falar, mas elas merecem atenção especial, ou seja, um post só para elas), vamos ver com mais detalhes como as variáveis 'caminham' entre as funções.

Como vimos no post anterior desta série (aqui), as variáveis declaradas entre as {} de uma função são locais à função, ou seja, não são acessíveis por outras funções do programa. No entanto, vimos, aqui, que é possível passar o retorno de uma função, que geralmente é uma variável local da função, para outras funções. Como isso acontece?

Não te conheço, mas sei o que você tem!
A forma correta de chamar uma função é passando os argumentos para os parâmetros que ela utiliza para fazer o seu trabalho. No exemplo que vimos na 1a parte desta série, para somar dois números, na linha 25 passamos as variáveis num1 e num2 como argumentos para a função soma_int que utiliza as variáveis val1 e val2 para fazer o seu trabalho.
Na verdade não passamos as variáveis, e sim o seu conteúdo. O que acontece realmente é que o conteúdo da variável num1 é copiado para a variável val1, e o conteúdo da variável num2 é copiado para a variável val2.
Esta operação é chamada de passagem por valor, porque os valores armazenados nos argumentos são copiados para os parâmetros.
Uma vez que a função trabalha com uma cópia do conteúdo das variáveis num1 e num2, o seu conteúdo permanece intacto, e a função soma_int consegue fazer o seu trabalho corretamente (todos ficam satisfeitos rsrs).
Mas, e se precisarmos alterar o conteúdo das variáveis num1 e num2? Seria possível?
Vejamos isto na prática. Observe o código abaixo.


//TROCANDO O CONTEÚDO DE VARIÁVEIS USANDO FUNÇÕES
#include <stdio.h>
#include <stdlib.h>
 
int ler_int(){
 int num;
 
 printf("Digite um numero inteiro: ");
 scanf("%d", &num);
 return num;
}
 
void troca(int val1, int val2){
 int aux;
 
 aux = val1;
 val1 = val2;
 val2 = aux;
}
 
void exibe_int(int val1, int val2){
 printf("\nO valor de num1 = %d e num2 = %d.\n", val1, val2);
}
 
int main(){
 int num1, num2;
 
 num1 = ler_int();
 num2 = ler_int();
 exibe_int(num1, num2);
 troca(num1, num2);
 exibe_int(num1, num2);
 system("pause");
}


Antes de executar este código, vamos falar sobre a função troca, que aparece na linha 13. Na linha 14, declaramos uma variável aux, que usaremos para salvar o valor armazenado na variável val1 antes de armazenar nela o valor da outra variável, val2, como mostrado nas linhas 16 e 17. O valor salvo em aux, ou seja o valor anterior de val1, será agora armazenado em val2.
Agora que entendemos como funciona a função troca, pode executar o código e observar o resultado.
Acredito que você já sabe o porquê dele não funcionar. Se não sabe, veja a razão.
Quando a função troca é chamada na linha 31, dentro da main, ela recebe os valores das variáveis num1 e num2, que são copiados para as variáveis val1 e val2 que são os seus parâmetros. Na verdade, os valores que são trocados são os de val1 e val2, e não os valores de num1 e num2 como desejado.
Para resolver situações como estas, entra em ação um recurso que já conhecemos: os ponteiros.

Não adianta se esconder... Eu sei onde você mora!
Se você não se recorda do funcionamento de um ponteiro, dê uma olhada aqui.
Agora observe como fica o nosso código usando este recurso.


//TROCANDO O CONTEÚDO DE VARIÁVEIS USANDO FUNÇÕES
#include <stdio.h>
#include <stdlib.h>
 
int ler_int(){
 int num;
 
 printf("Digite um numero inteiro: ");
 scanf("%d", &num);
 return num;
}
 
void troca(int* val1, int* val2){
 int aux;
 
 aux = *val1;
 *val1 = *val2;
 *val2 = aux;
}
 
void exibe_int(int val1, int val2){
 printf("\nO valor de num1 = %d e num2 = %d.\n", val1, val2);
}
 
int main(){
 int num1, num2;
 
 num1 = ler_int();
 num2 = ler_int();
 exibe_int(num1, num2);
 troca(&num1, &num2);
 exibe_int(num1, num2);
 system("pause");
}


Agora funcionou! Você sabe me dizer porquê?
Este uso de ponteiros para tornar possível o acesso a variáveis locais de outras funções é chamado de passagem por referência. Porque na verdade, não se passa o valor da variável, mas a sua referência, ou seja, o seu endereço na memória, que como sabemos, torna possível acessar e alterar esta variável.
Viu como é fácil (os ponteiros são nossos amigos rsrs).
Até a próxima.

sábado, 12 de dezembro de 2009

Dividir para conquistar! Parte II (local x global)

Continuando o que começamos aqui, hoje, como prometido, vamos falar sobre o 'comportamento' das variáveis quando usamos modularidade.

Vamos brincar de esconde-esconde?
Existe um conceito em C chamado de escopo. Já procurei uma definição literal para a esta palavra e não encontrei. Então como costumo fazer, vamos à pratica.


#include "stdio.h"
#include "stdlib.h"
 
int b = 5;
 
int main(){
 int a, c;
 
 a = 6;
 c = a + b;
 printf("\na = %d, b = %d e c = %d.\n",a,b,c);
 {
    int a, c;
 
    a = 15;
    c = a + b;
    printf("\na = %d, b = %d e c = %d.\n",a,b,c);
    b = c;
 }
 c = a + b;
 b = 2 * a;
 printf("\na = %d, b = %d e c = %d.\n",a,b,c);
 system("pause");
}


Observe o código acima. Aliás, não só observe, fique curioso e coloque ele para rodar no seu micro. Já colocou? Viu a saída?
Vamos entender.
A princípio só vemos declarações de variáveis e atribuições. Mas na linha 12 vemos algo diferente (que acredito não ser comum em códigos fonte profissionais, mas funciona rsrs). Como sabemos, as {} separam um bloco de comandos, geralmente estes blocos estão ligados a instruções de decisão e repetição (if, while, for), e também a funções; mas elas também funcionam sozinhas, como mostrado neste código.
Observe os comandos das linhas 7 e 13, e das linhas 10 e 16. São os mesmos comandos, só as variáveis envolvidas que são diferentes, apesar de terem os mesmos nomes.
Como já vimos, aqui, os nomes das variáveis são 'etiquetas' (rótulos) que identificam endereços de memória. Apesar de estarem usando a mesma 'etiqueta', os endereços das variáveis declaradas nas linhas 7 e 13 são diferentes. O C não se confunde por causa das {}.
Quando são feitas as operações dentro das {}, são usadas as variáveis declaradas dentro das {}. Assim o valor da variável a que é impresso no printf da linha 11 é um, e o da linha 17 outro. O valor de a impresso pelo printf na linha 21 é o mesmo impresso pelo printf da linha 11, pois é a mesma variável. A atribuição da linha 15 não altera o seu valor, já que ela altera outra variável, que por acaso (neste caso, propositalmente) tem o mesmo nome. Dizemos que as duas variáveis tem escopo diferente.
Quando se trata de funções, dizemos que a variável declarada entre as {} do bloco de comandos de uma função é uma variável local da função. Ela fica 'escondida' dentro das {}, e para todas as outras funções é como se ela nem existisse.
Lembra do que já falamos sobre funções. Uma função é uma 'caixa preta', não precisamos saber como ela faz as coisas, apenas os parâmetros que ela precisa e o retorno que fornece (isto quando ela precisa de parâmetros e dá algum retorno). Ela pode usar uma série de variáveis, mas não precisamos tomar conhecimento da existência delas.
Em oposição a isto, existe um tipo de variável chamada de global. Temos um exemplo de uma variável assim na linha 4 do nosso código exemplo acima: é a variável b.
Ela é declarada e inicializada fora de qualquer bloco, fora até mesmo da função main. Observe que utilizamos o valor de b para fazer a operação de soma da linha 10, e usamos este mesmo valor na soma da linha 16, que está dentro das {}.
Por ser global, esta variável é 'vista' por qualquer parte do programa (funções, inclusive), e também pode ser alterada em qualquer parte do programa.
Fazemos uma alteraçao no valor da variável b na linha 18, que está dentro das {}, e este novo valor de b é usado na soma que é realizada na linha 20, fora das {}, e depois alterado na linha 21.
Até o próximo post.

domingo, 6 de dezembro de 2009

Dividir para conquistar! Parte I (funções)

Até o presente momento vimos exemplos de códigos fontes pequenos, com uma utilidade muito pequena (coisa muito difícil de encontrar no 'mundo real'), e portanto, muito fácil de acompanhar, compreender o seu funcionamento e eliminar os seus erros.
Eu já disse algumas vezes aqui no blog que é fácil encontrar códigos fonte com mais de 1000 linhas. Imagine a dificuldade para acompanhar um código desses!
E imagine a quantidade de tempo que 1 programador levará para concluir este código!
Para resolver estas questões o C usa uma metodologia chamada de modularidade.

O que é modularidade?
Imagine que você fosse o chefe de um grupo que deveria criar uma maquete de uma fábrica. Lógico que você iria dividir este trabalho de criação em várias partes. Dividindo entre os componentes do grupo cada uma dessas partes de acordo com o que cada um sabe fazer. Não seria inteligente deixar alguém fazer algo que não é sua especialidade tendo na equipe um especialista nesta tarefa.
Isto é modularidade!
Dividir uma tarefa grande em várias partes menores e especializadas. Quando digo especializada, quero dizer que idealmente, cada módulo fará uma única tarefa.(O que você prefere? Ser atendido por um médico especializado na sua doença ou por um clínico geral?)
Cada um desses módulos é chamado de função.
As funções podem ser classificadas de acordo com seus parâmetros e retorno.

O que são parâmetros? E retorno?
Algumas funções precisam de dados para cumprir a tarefa para a qual foram criadas. Esses dados são chamados de parâmetros da função.
Voltando ao exemplo da maquete, se você quiser um prédio em sua maquete, terá que dizer a quem irá fazê-lo quantos andares ele terá, qual a cor dele, se terá entrada lateral, etc. Se essas informações não forem passadas, o responsável não saberá como fazê-lo, ou poderá fazê-lo da forma que ele quiser, que pode não ser a que você desejava.
De posse dessas informações (argumentos), o prédio será construído conforme especificado, seguindo esses parâmetros.
Existem funções que precisam de vários parâmetros, outras que precisam de apenas 1 parâmetro, e ainda, as que não precisam de nenhum paramêtro.
Se você quisesse a sua maquete bem realista, poderia pedir a um componente do grupo que fizesse alguns carros populares para ficar no estacionamento da fábrica. Este componente é especialista em fazer carros populares, portanto, não é necessário passar nenhuma informação para ele. Ele fará os carros e entregará.
Já o retorno é o dado que a função devolve depois de fazer o seu trabalho.
No exemplo da maquete, seria o prédio e os carros.
Diferente dos parâmetros, o retorno só permite duas configurações: 1 ou nenhum.
Você pode se perguntar: Mas não seriam vários carros?
Você está certo. O que acontece na verdade, é que o responsável por fazer os carros, os faz um a um. E também os entrega um a um.
Neste exemplo da maquete, você é o chefe da equipe, o coordenador do projeto. Você passa a tarefa para o componente, e ele devolve o trabalho pronto. Você apenas agrupa o resultado. Pode ser que inclusive, você tenha que passar o resultado final do trabalho de um componente para um outro componente como a matéria-prima deste.
Num programa em C, esta função de coordenação é feita pela função main (sim, a main é uma função!).
Vamos ao exemplo de um programa que faz a soma de 2 números inteiros digitados pelo usuário usando este conceito de modularidade.
Antes de ver o código propriamente dito, vamos pensar nas funções que precisamos para executar essa tarefa.
1o precisamos de uma função que receba os valores inteiros digitados pelo usuário e devolva estes valores. Como sabemos ela não pode devolver os 2 valores de uma só vez. Então ela deve ler um valor e retornar este único valor. Depois ela será executada de novo para o outro valor.
Esta função precisa de algum parâmetro? A resposta é NÃO. Ela é especializada em ler inteiros, e inteiros são sempre inteiros. Acredito que ela não necessite de nenhum. E tem algum retorno? Com certeza. São os valores digitados pelo usuário.

2o precisamos de uma função que faça a soma propriamente dita.
Esta função precisa de parâmetros? Com certeza. São os valores digitados pelo usuário, que vieram da função anterior.
E tem algum retorno? Com certeza. É o resultado da soma.

3o precisamos de uma função que mostre ao usuário o resultado da soma.
Esta função precisa de algum parâmetro? Com certeza. É o resultado da soma retornado pela função anterior.
E tem algum retorno? A resposta é NÃO. Já que o valor vai ser mostrado e nada mais será feito no programa, não existe esta necessidade.

Vamos ver uma representação gráfica disto:



As setas indicam 'o caminho' das variáveis. As vermelhas indicam as variáveis que as funções retornam para a função main, e as pretas indicam as variáveis que a função main passa para as funções. (Eu iria usar outras cores, mas não poderia deixar de fazer esta homenagem ao HEXA DO MENGÃO! rsrs)

Uma função em C, é declarada da seguinte forma:

 <tipo de retorno> nome da função (<tipo do parâmetro> nome do parâmetro, <tipo do parâmetro> nome do parâmetro, ..., <tipo do parâmetro> nome do parâmetro){
    comandos da função
}


Vamos ao código, que eu explico os detalhes depois.


//EXEMPLO DO USO DE FUNÇÕES
#include <stdio.h>
#include <stdlib.h>
 
int ler_int(){
 int num1;
 printf("Digite um numero inteiro: ");
 scanf("%d", &num1);
 return num1;
}
 
int soma_int(int val1, int val2){
 return val1 + val2;
}
 
void exibe_result(int resultado){
 printf("O resultado da operacao eh %d.\n", resultado);
}
 
int main(){
 int num1, num2, result;
 
 num1 = ler_int();
 num2 = ler_int();
 result = soma_int(num1, num2);
 exibe_result(result);
 system("pause");
}


A 1a função, que se encontra na linha 5, é a ler_int. Pelo seu cabeçalho, vemos que é uma função que devolve um valor inteiro e não necessita de nenhum parâmetro. Vejamos o que esta função faz.
Na linha 6 é declarada uma variável inteira num1, que será usada para receber o valor digitado pelo usuário e retornar este valor para a função main.
Observe a linha 7 onde é mostrada ao usuário uma mensagem indicando o que ele deve fazer. Poderíamos ter algum código para verificar se o valor digitado está dentro de uma faixa específica, dentre outras coisas.
Na linha 8, o valor digitado pelo usuário é armazenado na variável inteira num1 usando o scanf. Até aqui não há nada que já não vimos antes.
A novidade está na linha 9. Observe o uso da palavra reservada return. É assim que é feito o retorno (será por isso que ele tem esse nome?) do valor para a função main. Neste caso, o valor retornado será o que está armazenado na variável inteira num1.
Na linha 12, temos a nossa 2a função, que é a soma_int. Esta função retorna um valor inteiro e necessita de 2 parâmetros inteiros (não é obrigatório que os parâmetros sejam do mesmo tipo retornado pela função, poderia ser de qualquer tipo e em qualquer quantidade, como já vimos).
Um outro detalhe interessante está na linha 13. Observe que é retornado o resultado da soma dos valores passados como parâmetros. Poderíamos ter declarado uma variável inteira, ter atribuído o resultado desta soma à variável e, só então, retornar o valor armazenado nela. A escolha de uma forma ou outra é exclusiva do programador. Eu, particularmente, prefiro como se encontra no código.
A 3a função, exibe_result, que aparece na linha 16, já começa com uma novidade.
Como vimos acima, esta função não retorna nenhum valor. A forma em C de especificar isto é usando o tipo void (lembra dele?). Isto significa que a função não retorna NADA. Além disto, ela necessita de 1 parâmetro, que é o que vai ser exibido, neste caso o resultado da soma que é retornado pela função soma_int.
A última função deste código é a main, que aparece na linha 20. Como já falei antes é ela que coordena o 'trabalho' das outras funções. Vamos ver como ela faz isto.
Na linha 21 são declaradas 3 variáveis inteiras. Elas são necessárias para receber o valor retornado pelas funções (se uma função devolve um valor, ele tem que ser armazenado em algum lugar ou será perdido).
Observe que uma destas variável tem o nome de num1, o mesmo nome daquela variável usada pela função ler_int, lá na linha 6. Antes que você reclame, deixe-me falar uma coisa: elas tem o mesmo nome, mas são a mesma 'pessoa' (rsrs).
A variável num1 de ler_int, só 'existe' enquanto a função ler_int está em execução. E enquanto isto a variável num1 da main, fica 'desativada'.
Dizemos que essas variáveis são locais. Mais a frente falaremos detalhadamente sobre isto. Por enquanto basta saber isto.
Na linha 23, está a 1a chamada da função ler_int. Este é o nome técnico usando para indicar que uma função será executada. Observe que é feita uma atribuição nesta mesma linha. Como já falamos, é para salvar o retorno da função.
Quando não existe um retorno, a forma da chamada é igual a da linha 26: somente o nome da função e os argumentos (os valores que são passados para preencher os parâmetros de uma função são chamados de argumentos) que ela necessita, se ela necessitar de algum.
Acredito que o restante do código agora ficou fácil de entender.
Uma outra coisa interessante a respeito das funções, é que é possível passar o retorno de uma função diretamente como argumento para outra função, sem precisar armazená-lo em uma variável.
Olhe este código abaixo e veja se consegue entender o que ele faz.


//EXEMPLO DO USO DE FUNÇÕES
#include <stdio.h>
#include <stdlib.h>
 
int ler_int(){
 int num1;
 printf("Digite um numero inteiro: ");
 scanf("%d", &num1);
 return num1;
}
 
int soma_int(int val1, int val2){
 return val1 + val2;
}
 
void exibe_result(int resultado){
 printf("O resultado da operacao eh %d.\n", resultado);
}
 
int main(){
 
 exibe_result(soma_int(ler_int(), ler_int()));
 system("pause");
}


Fácil não é?
Até a próxima.

quarta-feira, 2 de dezembro de 2009

Tá apontando o quê? (ponteiros)

Além dos tipos básicos e dos tipos estruturados (que vimos aqui e comentamos alguma coisa aqui), o C possui um tipo de dados especial: o tipo ponteiro.

A função principal do ponteiro é ... APONTAR (não é brincadeira!).
Vamos ver como isso funciona.

Eu sei onde você está!
Podemos imaginar um ponteiro como uma posição na memória onde está armazenado algum valor que pode ser alterado. Então um ponteiro é uma variável?

SIM e NÃO (rsrs).

A diferença entre um ponteiro e uma variável está no tipo de valores que eles podem armazenar. Enquanto uma variável pode armazenar caracteres, números inteiros e reais; um ponteiro armazena endereços de memória.
No post em que falamos sobre vetores (aqui), vimos a relação entre as variáveis e a sua posição na memória. Observe a figura abaixo (que é a última daquele post).



Se você clicou no link e viu o post sobre vetores, irá reparar que a numeração abaixo dos 'quadrinhos' está diferente. Por quê?

É diferente... mas é a mesma coisa!
Enquanto no referido post os números abaixo dos 'quadrinhos' indicavam os índices do vetor, neste se referem aos endereços de memória.
Um ponteiro que apontasse para o 3o elemento deste vetor estaria armazenando o valor 52.
Repare que os valores de endereço aumentam (ou diminuem) de 4 em 4. Isto acontece porque, neste exemplo, são necessários 4 bytes para armazenar um valor inteiro. Portanto, o ponteiro de que falamos acima é um ponteiro para inteiros. Se fosse um vetor de caracteres, onde cada caracter é armazenado em 1 byte, precisaríamos de um ponteiro para caracteres.

E qual é a vantagem de usar um ponteiro?
Antes de ver um ponteiro em ação (rsrs), vamos ver a sua sintaxe.

 int *pint;

O comando acima declara uma variável do tipo ponteiro para inteiros com o nome pint. Se quisermos apontar para um float o comando seria:

 float* pfl

Não é necessário que o nome do ponteiro comece com p (embora seja uma boa prática), nem que tenha o tipo da variável. Só deve ter o tipo e o *, que pode estar tanto junto ao nome como ao tipo.

Uma das vantagens no uso de um ponteiro está no fato de que além de apontar para o endereço de uma variável, também é possível ter acesso ao conteúdo desta variável usando o ponteiro. A esta operação se dá o nome de dereferenciar um ponteiro.

Como sempre, vamos ver isso na prática.


//EXEMPLO DO USO DE UM PONTEIRO
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int* pnum;
 int num = 10;
 
 pnum = &num;
 printf("O valor inicial de num eh %d.\n",*pnum);
 *pnum += 2;
 printf("O valor atual de num eh %d.\n",num);
 num--;
 printf("O valor final de num eh %d.\n",*pnum);
 system("pause");
}


O código acima é muito simples, mas mostra alguns fundamentos muito importantes. Vamos entender o que ele faz.
Na linha 6 definimos uma variável ponteiro para inteiros chamada de pnum. Na linha 7 definimos uma variável inteira num e a inicializamos com o valor 10.
Um ponteiro armazena endereços de memória. E é isso que fazemos na linha 9. Atribuimos o endereço da variável inteira num ao ponteiro para inteiros pnum.
Observe que a variável é do mesmo tipo do ponteiro. Se não fosse esta atribuição seria NÃO poderia ser feita.
Na linha 10 exibimos o valor de num dereferenciando o ponteiro pnum.
Além de acesso ao conteúdo da variável, também podemos alterá-lo usando o ponteiro, o que é mostrado na linha 11 e comprovado na linha 12. Acredito que agora você já saiba o que fazem as outras linhas (rsrs).
Você deve estar se perguntando o por quê de usar um ponteiro ao invés da própria variável. Isto vai se tornar claro quando falarmos sobre modularidade, que é a possibilidade de dividir um programa em várias partes, sendo que as variáveis de cada parte estão 'escondidas' das outras partes.

Outro uso muito comum de ponteiros é como uma forma alternativa de acessar elementos de um vetor. Esse acesso é feito através da aritmética de ponteiros.

Vetor x Ponteiro
Os dados armazenados numa variável do tipo ponteiro são endereços de memória (quantas vezes eu já repeti isso???). Esses endereços são sempre valores numéricos. E com esses valores podemos fazer algumas operações aritméticas (SOMENTE SOMA E SUBTRAÇÃO).
Observe este dois comandos aplicados a um ponteiro para inteiro pnum:

 (*pnum)++;
 pnum++;

O 1o comando incrementa o conteúdo da posição de memória apontada por pnum, ou seja, se na posição apontada por pnum estiver um inteiro com o valor 11, após este comando o valor deste inteiro passa a ser 12.
Já o 2o comando incrementa o valor armazenado em pnum. Como pnum é um ponteiro para inteiros, que estamos supondo necessitarem de 4 bytes para serem armazenados na memória, ao valor de pnum será somado 4.
Por exemplo, digamos que no código exemplo que usamos acima o endereço da variável inteira num fosse 38. Este valor foi atribuido ao ponteiro para inteiros pnum. Então o conteúdo de pnum é 38. Ao executar este 2o comando este conteúdo passa a ser 42. Ou seja, pnum não estará mais apontando para num, e passará a apontar para uma posição de memória na qual não sabemos o que está armazenado. Isto é extremamente perigoso, pois torna possível alterar algo que não deveria ser alterado.
Se para uma única variável isto é perigoso, para um vetor é um ótimo recurso, afinal um vetor é um grupo de variáveis do mesmo tipo armazenadas em sequência na memória.
Observe como ficaria o 2o exemplo do post sobre vetores utilizando aritmética de ponteiros.


//PROGRAMA QUE RECEBE 100 NÚMEROS INTEIROS
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int vetor[100], i;
 int* pvet;
 
 pvet = vetor;
 printf("Digite 100 numeros inteiros:\n");
 for(i=0; i < 100; i++){
    printf ("Valor %d: ",i+1);
    scanf("%d", pvet++);
 }
 system("pause");
}


Na linha 6 declaramos um vetor de 100 inteiros e uma variável inteira i que será usada para dar ao usuário uma noção de quantos valores ele já digitou. Na linha 7 definimos uma variável ponteiro para inteiros chamada pvet. Na linha 9 atribuimos o endereço do vetor ao ponteiro pvet (Numa atribuição, o nome de um vetor corresponde ao seu endereço). Na linha 13, dentro do for, é feita a leitura do valor digitado pelo usuário para a posição de memória apontada por pvet, que da 1a vez é a 1a posição do vetor, e depois o conteúdo de pvet é incrementado, passando a apontar para a próxima posição do vetor.

Viu como não é tão difícil quanto parece?

segunda-feira, 30 de novembro de 2009

Juntos somos mais eficientes! (vetores)

Até agora vimos somente os tipos básicos do C (aqui), e fizemos muita coisa com eles. Mas chega um momento em que eles não são eficientes para resolver alguns problemas. Quer um exemplo?
Imagine um programa que receba 100 valores inteiros digitados por um usuário. Imaginou? Se não, veja como ele seria usando o que vimos até agora.


//PROGRAMA QUE RECEBE 100 NÚMEROS INTEIROS
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int val001, val002, val003, val004, val005, ... , val100;
 
 printf("Digite 100 numeros inteiros:\n");
 scanf("%d",&val001);
 scanf("%d",&val002);
 scanf("%d",&val003);
 scanf("%d",&val004);
 scanf("%d",&val005);
    .
    .
    .
 scanf("%d",&val100);
 system("pause");
}


Percebeu a quantidade de linhas que esse programa teria? Sabia que é possível fazer este mesmo programa com 16 linhas (até menos) e de forma muito mais inteligente?
Dê uma olhada nesse exemplo.


//PROGRAMA QUE RECEBE 100 NÚMEROS INTEIROS
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int vetor[100], i;
 
 printf("Digite 100 numeros inteiros:\n");
 for(i=0; i < 100; i++){
    printf ("Valor %d: ",i+1);
    scanf("%d",&vetor[i]);
 }
 
 system("pause");
}


Este código acima está completo e funciona perfeitamente (não faz muita coisa, é verdade, mas funciona ;). Vamos ver o 'segredo' por trás disto.

Observe a linha 6. Nela é declarado um vetor de 100 inteiros. Vetor???

Um grupo de iguais.

Um vetor é um tipo estruturado homogêneo.(???)
Estruturado significa que ele é uma composição de tipos básicos, ou seja, tipos básicos organizados em um grupo/agrupamento. E homogêneo, significa que esses tipos básicos são iguais; no nosso exemplo, são 100 inteiros (é possível criar agrupamentos heterogêneos, como veremos em outro momento).
Não entendeu ainda? Quer que eu desenhe?



A figura acima representa um vetor de 4 inteiros armazenado na memória. Observe que o armazenamento é consecutivo, ou seja, cada valor inteiro está armazenado num endereço da memória, e estes endereços estão em sequência. Se tivéssemos declarado 4 variáveis inteiras não poderíamos garantir que elas ficariam armazenadas em sequência na memória.
Por estarem armazenados em sequência na memória, podemos acessar o conteúdo de cada endereço também em sequência. Esse é o 'truque' do vetor.
Mas como é feito esse acesso?

Leia o índice!

Com certeza você já teve contato com algum livro (se não, aconselho que o faça rsrs). Podemos generalizar um livro como uma sequência de páginas (escritas ou não) agrupadas. Todas as páginas tem o mesmo tamanho (não me lembro de ter visto algum em que isto não fosse verdadeiro).
Quando queremos encontrar alguma informação específica num livro temos duas opções: ou folheamos o livro, página por página, analisando uma a uma; ou vamos ao seu índice e localizamos o número exato da página onde está a informação que procuramos. Qual delas você considera mais eficiente?
Em termos de comparação, tirando as muitas diferenças, um vetor se assemelha muito a um livro. São posições de memória do mesmo tamanho agrupadas contendo algum valor válido (ou não). Já que são tão parecidos, por que não usar um índice também com um vetor? Facilitaria muito, não concorda? Pois é exatamente assim que funciona.
Vamos a outro desenho.



Para acessar o conteúdo da 1a posição do vetor da figura acima, é só se referir ao nome do vetor e ao índice 0 (zero). Os vetores em C SEMPRE começam com o índice 0 (zero).
Digamos que o nome do vetor da figura acima seja vet. Para exibir o conteúdo da sua 1a posiçao, usamos o comando:

 printf("%d", vet[0]);

Seria impresso o valor 67.

Fácil, não? Só devemos prestar atenção a 2 pontos importantíssimos:

1o. O primeiro índice de um vetor em C SEMPRE começa em 0 (zero)
2o. O último índice de um vetor em C é SEMPRE o tamanho do vetor menos 1.

Isso não significa que não se possa acessar um elemento fora do vetor.
Se usássemos no vetor dos desenhos acima o comando:

 printf("%d",vet[4]);

Algo seria impresso na tela. Não temos como saber o que seria, mas que funcionaria, funcionaria. Por quê?

Um nome de um vetor seguido de um índice i significa que queremos acessar o endereço que está i posições depois do endereço inicial do vetor.
Se um vetor de 4 inteiros está armazenado na memória, por exemplo, a partir do endereço 44 e o tamanho de um inteiro é 4 bytes; o elemento de índice 3 deste vetor estará no endereço 56.

 endereço inicial do vetor(44) + 3 * (tamanho do tipo do vetor (4)

Se tentarmos acessar o índice 4, que sabemos 'não existir', na verdade estaremos acessando o endereço 60 da memória, e 'interpretando' o seu conteúdo como um valor inteiro (este conteúdo pode ser qualquer coisa que esteja na memória, possivelmente deixada por algum outro programa, frequentemente no referimos a ele como 'lixo').
Algumas linguagens de programação não permitem esse tipo de acesso, mas o C não faz restrições. É sua responsabilidade como programador evitar esse tipo de coisa.

Agora que conhecemos o tipo vetor, ficou fácil entender o código do nosso exemplo. Certo?

quarta-feira, 25 de novembro de 2009

Um passo de cada vez... Algoritmos (fatorial/fibonacci)

Apesar do título do post ser um passo de cada vez, hoje veremos 2 algoritmos clássicos: Fatorial e Números de Fibonacci.
A razão para vermos os 2 ficará clara mais adiante em nossos estudos. Sem mais conversa vamos a eles.

Fatorial
Se você não sabe do que se trata, veja aqui.
Um exemplo de código que calcula o fatorial de um número em C, pode ser visto abaixo.


//PROGRAMA PARA CALCULAR A FATORIAL DE UM NUMERO DIGITADO
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int cont = 1, num, fatorial = 1;
 printf("Digite um numero inteiro:\n");
 scanf("%d",&num);
 while (cont <= num){
    fatorial *= cont;
    cont++;
 }
 printf("O fatorial de %d e %d\n",num,fatorial);
 system("pause");
}


Definimos e inicializamos uma variável inteira que chamamos de fatorial. Na linha 10, armazenamos nela o seu valor anterior multiplicado pelo valor da variável inteira contador, que teve o seu valor inicial definido como 1 (um), e é incrementado a cada repetição até alcançar o valor digitado pelo usuário.
Se o usuário digitar o número 4, por exemplo, haverá 4 repetições do bloco de comandos controlado pelo while, que resultará em:
 1 * 2 * 3 * 4
O resultado desta operação será o valor 24, que o fatorial do número 4.
Simples assim ;).

Sequência de Fibonacci
Antes de olhar o código, dê uma olhada aqui.
Agora que você já sabe o que é (sabe mesmo?), vamos a um exemplo em C.


//PROGRAMA QUE IMPRIMA OS N PRIMEIROS TERMOS
//DA SEQUENCIA DE FIBONACCI.
#include <stdio.h>
#include <stdlib.h>
 
int main(){
  int i, termos, termo, par = 1, impar = 1;
 
 printf("Digite o numero de termos desejado:\ ");
 scanf("%d",&termos);
 printf("\n");
 if (termos > 0){
    printf("0");
    i = 1;
    while (i<termos){
       if (i<3)
          printf(",1");
       else{
          termo = par + impar;
          printf(",%d",termo);
          if (i%2 == 0)
             par = termo;
          else
             impar = termo;
       }
       i++;
    }
 }
 else
    printf("NUMERO INVALIDO!!!\n");
 printf(".\n");
 system("pause");
}


Não sei se você reparou, mas observando a sequência de Fibonacci podemos ver que os termos de ordem par são a soma do termo ímpar e do termo par imediatamente anteriores a ele, exceto para os 2 primeiros termos, que por definição são 0 e 1.
Observe a figura abaixo.



Partindo desta observação, definimos duas variáveis inteiras chamadas de par e ímpar para armazenar os termos par e ímpar atuais, e assim, executar a soma dos mesmos para obter o próximo termo. Se este próximo termo for par, armazenamos ele em par, se for ímpar, em ímpar (acredito que isto seja lógico ;).
O if da linha 16 é para garantir que os 2 primeiros termos sejam 0 e 1, atendendo a definição.

Alguma dúvida? Pode perguntar nos comentários.
Por hoje é só. Até a próxima.

quarta-feira, 18 de novembro de 2009

Um passo de cada vez... Algoritmos (números primos)

Hoje vamos analisar um algoritmo para determinar se um número é primo.
Você sabe o que é um número primo? Veja aqui.
Agora que sabemos o que é um número primo, vamos ao algoritmo?
Antes, uma pequena observação: o algoritmo apresentado aqui tem como único e exclusivo objetivo ensinar o princípio do seu funcionamento, portanto, não está focado na velocidade e eficiência. Ok?
Dito isto, vamos ao código.


//PROGRAMA PARA VERIFICAR SE UM NÚMERO É PRIMO
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int num, nao_primo=0, divisoes=0, i;
 
 printf("Digite um numero inteiro: ");
 scanf("%d",&num);
 if ((num == 1) || (num % 2 == 0) && (num != 2))
    nao_primo = 1;
 else
    for(i = 1; (i <= num) && (divisoes <= 2); i += 2){
       if (num % i == 0)
          divisoes++;
    }
 if ((nao_primo == 0) && (divisoes <= 2))
    printf("O numero %d eh primo!\n",num);
 else
    printf("O numero %d nao eh primo!\n",num);
 system("pause");
}


Na linha 10 verificamos se o número digitado é 1 ou se é um número par diferente de 2. Se for, já sabemos, por definição, que não é um número primo, e informamos isto na linha 11.
Se o número digitado não se encaixar em nenhuma das opções anteriores, iremos testar, usando um comando de repetição for (linha 13), a quantidade de divisões exatas do número pelos números ímpares de 1 até o próprio número. A cada divisão exata incrementamos uma variável divisoes na linha 15. Se houverem mais de duas divisões exatas nem precisamos continuar testando, pois um número primo só é divisível por 1 e por ele mesmo, ou seja, duas divisões exatas.
Observou a forma que usamos para fazer a divisão somente pelos números ímpares? Incrementando a variável i que controla a quantidade de repetições do for de 2 em 2 (i += 2). Inteligente, não?
As outras linhas do código são apenas para informar se o número é primo ou não.
Viu como um código que parecia difícil, se tornou fácil?

Está gostando do blog? Comente, marque sua opinião nas reações abaixo, seja um seguidor do blog, siga o nosso twitter... Sua opinião é importante para sabermos se estamos no caminho certo, que é tornar o C mais fácil.
Até a próxima.

terça-feira, 17 de novembro de 2009

Um passo de cada vez... Algoritmos (palíndromo)

Agora que já conhecemos bastante coisa sobre o C, gostaria de tocar num ponto que complica a vida de muita gente que está iniciando em programação: os algoritmos.
Já comecei a falar sobre isso (aqui), e inclusive já vimos(?) na prática, embora eu não tenha falado sobre, como criar um algoritmo.
No último post da série sobre comandos de repetição, em um dos exemplos eu mostrei como criar um programa a partir de uma situação proposta. Quem vai executar as operações não somos nós, e sim o computador. O algoritmo é o 'roteiro' dos passos que ele irá seguir para executar essa função. E quem tem que 'dizer' a ele o que fazer somos nós, afinal, computadores não pensam (ainda!).

Pensando em algoritmos
Como já é costume nosso, vamos à prática.
Nosso 1o algoritmo consiste em verificar se um número digitado pelo usuário é palíndromo.
Antes de abrir o Dev e começar a digitar os includes, vamos entender o que é um número palíndromo. Veja aqui.
Muito bem agora que sabemos o que é um número palíndromo, como descobrir se o número digitado é ou não um deles?
Vamos usar o número 171 como exemplo (logo esse?). Este número tem 3 dígitos.

Se invertermos a ordem dos dígitos, teremos o mesmo número.

Então é só comparar o número com o seu 'inverso'.
Comparar números é fácil, já vimos isso com o if e os operadores relacionais.
Mas, como inverter o número?

Aprendendo a usar os restos
Para inverter os dígitos de um número, primeiro precisamos separar estes dígitos. No caso do nosso número com 3 dígitos, primeiro precisamos retirar 2 dígitos e ficar com 1 dígito que resta. Depois, dos 2 dígitos que retiramos antes, precisamos tirar 1 dígito e ficar com o outro dígito que resta. No final, só restará 1 dígito.
Percebeu a repetição de palavras derivadas de resto (resta e restará). O segredo está aí!
Vejamos um número menor, com 2 dígitos, por exemplo 17.
Se retirarmos o 1o dígito, neste caso o 1, restará o 2o digito, neste caso o 7.
Mas como 'retirar' um dígito do número? O resto é a resposta!
Se dividirmos o número 17 por 10, teremos como resultado 1 e um resto igual a 7. Melhor ainda, se utilizarmos o operador de módulo teremos o resto desta divisão fácilmente.
 digito = 17 % 10
O comando acima armazenará em digito o valor 7, que é o último dígito do número 17.
Uma vez que já temos o dígito que nos interessa, podemos eliminá-lo do número, para poder usar o mesmo artifício afim de 'retirar' outros dígitos. Para eliminar o último dígito de um número é só dividir este número por 10.
 num = 17 / 10
O comando acima armazenará em num o valor 1, desprezando o último dígito do número 17, neste caso o 7.
Se usarmos o módulo de 10 na variável num, que agora contém o valor 1, teremos um resultado 0 e um resto 1.
Viu como é fácil fazer com um número de 2 dígitos? Com números com mais dígitos a lógica é a mesma, só tem que ser repetida outras vezes dependendo da quantidade de dígitos.
Podemos usar o módulo de 10 para obter o último dígito do número, e depois dividir o número por 10 para eliminar este dígito que já foi obtido, e ir repetindo este processo até que o resultado da divisão do número por 10 seja 0 (zero), o que significa que acabaram os dígitos do número.
Já aprendemos como 'retirar' os dígitos de um número. E agora? Como inverter este número?
Vamos voltar ao nosso exemplo com o número 17. O inverso de 17 é 71. Que é o último dígito, neste caso o 7, multiplicado por 10, somado com o 1o digito, neste caso o 1.
Agora ficou fácil mesmo!
É só pegar o último dígito, neste caso o 7, obtido usando o módulo de 10 do número 17, multiplicar por 10, e somar com o 1o dígito, neste caso o 1, obtido usando o módulo de 10 no resultado da divisão do número 17 por 10.
Com números com mais dígitos a lógica é exatamente a mesma.
Vamos ver como vai ficar o nosso código?

//PROGRAMA PARA DETERMINAR SE UM NUMERO É PALINDROMO
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int digitado,num,invertido,digito;
 printf("Digite numero inteiro: ");
 scanf("%d",&digitado);
 num = digitado;
 invertido = 0;
 while(num != 0){
    digito = num % 10;
    invertido = (invertido * 10) + digito;
    num = num / 10;
 }
 if(digitado == invertido)
    printf("Palindromo !!!\n");
 else
    printf("Nao eh Palindromo!!!\n");
 system ("pause");
}


Os nomes das variáveis já falam por si. Note que não trabalhamos diretamente com o número digitado, e sim com uma variável num. Por quê?
Se tivéssemos usado diretamente o número digitado, ele seria perdido nas 'retiradas' dos dígitos e não teríamos como compará-lo com o seu inverso.
É mais fácil do que parece. Não é?
Até a próxima.

quinta-feira, 12 de novembro de 2009

De novo! De novo!... Final (curiosidades)

Para encerrar nossa série sobre os comandos de repetição, hoje vamos conhecer algumas 'curiosidades' sobre eles.

Tá faltando alguma coisa aí?
Acredito que você deve ter reparado no comando de repetição for, onde os comandos de controle de repetição do bloco ficam dentro de () separados por ;. Pois bem, você sabia que é possível usar um for sem que todas as suas partes estejam preenchidas? Lembra do exemplo que utilizamos para exemplificar o uso de um FLAG para sair de uma repetição (aqui)? Vamos refazer este exemplo, só que agora, utilizando o comando de repetição for, e você entenderá melhor como isto é possível.


//EXEMPLOS DE COMANDO DE REPETIÇÃO (FLAG)
#include <stdio.h>
#include <stdlib.h>
 
const int SAIR=0;
int main(){
 int num, total=0;
 
 printf("Digite um numero inteiro (0 (zero) para sair): ");
 scanf("%d",&num);
 for( ;num != SAIR; ){
    total += num;
    printf("Digite um numero inteiro (0 (zero) para sair): ");
    scanf("%d",&num);
 }
 printf("A soma dos valores informados eh igual a %d\n", total);
 system("pause");
}


O código deste exemplo é praticamente o mesmo que vimos antes, a única alteração está na linha 11, onde vemos o for.
Observe que não existe a inicialização de uma variável contadora, já que não temos um número de repetições determinado. Observe, também, que não existe um incremento.
Temos somente o comando que avalia se o bloco de comandos será repetido ou não.
Pode compilar e executar este código. Você verá que ele faz, exatamente, a mesma coisa que a versão utilizando o comando de repetição while.
Agora, vamos ir um pouco mais longe. Temos um comando dentro dos () do comando for. Poderíamos não ter nenhum?
Resposta: SIM. Um for sem nenhum comando dentro dos seus () cria o que se chama de loop infinito.
Por quê? Por quê? Por quê?
Já ouviu falar na Lei da Inércia? O loop infinito é basicamente isto. Se não dissermos ao comando for quando ele deve parar, ele vai continuar repetindo o bloco 'eternamente'.
E qual a utilidade disto??? Vamos a outro exemplo:


//EXEMPLO DE UTILIZAÇAO DE UM LOOP INFINITO
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int opcao, num, dobro, quadrado;
 
 for(;;){
    printf("Selecione uma das opcoes abaixo:\n");
    printf("1. Calcular o dobro de um numero\n");
    printf("2. Calcular o quadrado de um numero\n");
    printf("3. Encerrar o programa\n");
    printf("\nOpcao: ");
    scanf("%d",&opcao);
    system("cls");
    if (opcao == 1){
       printf("Digite um numero inteiro: ");
       scanf("%d",&num);
       printf("O dobro de %d eh %d.\n",num, num*2);
       system("pause");
       system("cls");
    }
    else
       if (opcao == 2){
          printf("Digite um numero inteiro: ");
          scanf("%d",&num);
          printf("O quadrado de %d eh %d.\n",num, num*num);
          system("pause");
          system("cls");
       }
       else
          if (opcao == 3){
             printf("\nO programa sera encerrado.\nObrigado por utilizar nossos sistemas!\n");
             system("pause");
             break;
          }
          else {
             printf("\nOPCAO INVALIDA!!! Tente novamente.\n");
             system("pause");
          }
    system("cls");
 }
}


Olhe bem para este código. Acredito que ele seja 'familiar' para você. Se não é, dê uma olhada aqui. Se você compilar e executar este código acima, verá que ele faz a mesma coisa que o exemplo que vimos para o comando de repetição do while.
Mas podemos notar várias diferenças entre eles, certo?
A 1a delas é que este não utiliza um do while, e sim, um loop infinito, utilizando um for 'sem comandos'.
A 2a é que este não utiliza um comando de decisão switch, e sim, vários comandos de decisão ifs aninhados.
Pergunta: Por que não usar o switch já que é muito mais prático?
A resposta para isto está na linha 35. É o comando break. O comando switch faz uso do comando break para sair do bloco de decisão depois que são executados os comandos referentes a uma única escolha. Só que neste caso, o comando break é usado para encerrar o loop infinito, e permitir o término do programa. Se tivéssemos usado o switch, o break nunca seria associado ao for do loop infinito, e sim, ao switch.
Olhando para este código, podemos ver porque o comando do while foi criado (rsrs).
Vamos a uma última 'curiosidade' (pelo menos por hoje).

Tem coisa demais aí?
Se por um lado, podemos não ter comando nenhum dentro dos () de um for; por outro, podemos ter mais de um comando entre os ; dentro dos () do for.
Antes de vermos o código de exemplo, vamos imaginar a seguinte situação:
Uma dupla está numa competição onde uma das provas consiste em encher um recipiente A de 3 litros com um líquido contido num recipiente B de 4,25 litros, percorrendo um circuito e utilizando dois outros recipientes menores de 500ml e 350ml, um para cada competidor da dupla respectivamente. Cada vez em que o líquido é levado do recipiente B para o A, é contada uma volta no circuito. A dupla tem que encher o recipiente A em no máximo 5 voltas. Os dois competidores podem levar o líquido do recipiente B para o A usando o seu respectivo recipiente na mesma volta. Devido as dificuldades no circuito, nem sempre os recipientes menores chegarão cheios ao recipiente A. Se algum dos competidores tentar fraudar a prova, informando um valor maior do que o seu recipiente pode transportar, o valor informado será ignorado. Como fazer um programa que controle isto???
Este é o nosso exemplo. Vamos a ele.


//PROGRAMA DE CONTROLE DA EXECUÇÃO DE UMA PROVA DE UMA COMPETIÇÃO
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int voltas;
 float liqA, total = 3000, rec500, rec350;
 
 for(voltas = 0, liqA = 0, rec500 = 0, rec350 = 0; liqA < total && voltas < 5; voltas++){
    printf("VOLTA %d:\n",voltas + 1);
    printf("Digite a quantidade em ml de liquido no recipiente de 500ml: ");
    scanf("%f",&rec500);
    if (rec500 <= 500)
       liqA += rec500;
    rec500 = 0;
    printf("Digite a quantidade em ml de liquido no recipiente de 350ml: ");
    scanf("%f",&rec350);
    if (rec350 <= 350)
       liqA += rec350;
    rec350 = 0;
    printf("\n");
 }
 if (liqA >= total)
    printf("\nA dupla cumpriu a tarefa em %d voltas e ganhou os pontos da prova.\n", voltas);
 else
    printf("\nA dupla so conseguiu transportar %.2f ml e nao ganhou os pontos da prova.\n", liqA);
 system ("pause");
}


Alguma dificuldade em entender este código?
Calma, vou explicar alguns detalhes dele.
Primeiro definimos as nossas variáveis. Com os nomes que demos a elas, não é difícil saber qual a função de cada uma delas.
Depois disto, dentro do nosso for, inicializamos todas que ainda não haviam sido inicializadas. Observe que para separar os vários comandos de inicialização de variáveis utilizamos a , (vírgula).
Depois definimos a condição para que o nosso for executasse a repetição do bloco de comandos, no caso, enquanto o líquido no recipiente A fosse menor que a volume desejado, e enquanto não tivessem acabado as 5 voltas.
No bloco de comandos do for, estão os comandos para receber os valores transportados e acumular no recipiente A. Os ifs asseguram que os valores de cada recipiente estão corretos, evitando uma possível fraude, já que só acumulam se eles estiverem corretos.
Os comandos após o for são para informar se a dupla conseguiu ou não completar a prova.
Viu como fica fácil depois que se sabe!!!
Até a próxima.

terça-feira, 10 de novembro de 2009

De novo! De novo!... Parte III (do while)

Vamos prosseguir nossa série sobre comandos de repetição. Hoje veremos o comando de repetição do while.

Faça primeiro. Avalie depois!
Existe um dito popular que fala que se deve pensar duas vezes antes de falar. Mas existem ocasiões em que é necessário executar algumas operações antes e avaliar o resultado delas depois. O exemplo mais clássico disto são as listas de opções ou menu (faça biquinho para pronunciar corretamente rsrs).
Nos comandos de repetição while e for, a avaliação é feita antes da 1a execução do bloco de comandos; já no comando de repetição do while, esta avaliação é feita depois que o bloco de comandos é executado a 1a vez, ou seja, pelo menos uma vez o bloco de comandos será executado.
A sintaxe do comando de repetição do while é esta:
 do {
    .
    .
    .
    } while (condição);


Como de costume, vamos a um código para exemplificar o uso do comando de repetição do while.


//EXEMPLO DE UTILIZAÇAO DO COMANDO DE REPETIÇÃO DO WHILE
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 int opcao, num, dobro, quadrado;
 
 do {
    printf("Selecione uma das opcoes abaixo:\n");
    printf("1. Calcular o dobro de um numero\n");
    printf("2. Calcular o quadrado de um numero\n");
    printf("3. Encerrar o programa\n");
    printf("\nOpcao: ");
    scanf("%d",&opcao);
    system("cls");
    switch (opcao){
       case 1:
          printf("Digite um numero inteiro: ");
          scanf("%d",&num);
          printf("O dobro de %d eh %d.\n",num, num*2);
          system("pause");
          system("cls");
          break;
       case 2:
          printf("Digite um numero inteiro: ");
          scanf("%d",&num);
          printf("O quadrado de %d eh %d.\n",num, num*num);
          system("pause");
          system("cls");
          break;
       case 3:
          printf("\nO programa sera encerrado.\nObrigado por utilizar nossos sistemas!\n");
          system("pause");
          break;
       default:
          printf("\nOPCAO INVALIDA!!! Tente novamente.\n");
          system("pause");
    }
    system("cls");
 }while (opcao != 3);
}


Observe que todos os comandos do programa estão dentro do bloco de comandos do comando de repetição do while, que começa na linha 8 e só acaba na linha 40. Nesta linha 40 está a avaliação da opção escolhida pelo usuário.
Primeiro, são apresentadas as opções disponíveis ao usuário, nas linhas de 9 a 12.
A linha 14 recebe a opção digitada pelo usuário, que é avaliada pelo comando de decisão switch da linha 16.
Enquanto a opção for diferente de 3 (sair), o bloco de comandos será executado.
Os outros comandos dentro do bloco de comandos do comando de decisão switch são para realizar a operação escolhida, apresentar o resultado desta operação, e ainda, permitir que o usuário repita sua escolha se fizer uma escolha inválida.
Fácil, não é?

Até a próxima.

sábado, 7 de novembro de 2009

De novo! De novo!... Parte II (while/contador/for)

Continuando nossa série sobre comandos de repetição, hoje veremos como repetir um bloco de comandos por um determinado número de vezes.

Faça isto todas essas vezes!
No post anterior, vimos como realizar a repetição de um bloco de comandos por um número indeterminado de vezes. Finalizando esta repetição quando um determinado valor fosse digitado (o famoso FLAG).
E quando sabemos exatamente quantas vezes queremos que um bloco seja repetido?
Imagine um programa que calcule a média aritmética de 15 valores inteiros digitados pelo usuário.
Já sabemos a quantidade de valores que serão digitados, neste caso, 15. Esta é a quantidade de vezes que devemos pedir ao usuário que digite um valor inteiro, ler este valor e somá-lo a um total para que, depois, possamos fazer a média dos valores digitados.
Vamos a um código para exemplificar isto.


//PROGRAMA QUE CALCULA A MÉDIA ARITMÉTICA ENTRE 15 VALORES INTEIROS
#include <stdio.h>
#include <stdlib.h>
 
const int MAXDIGIT=15;
int main(){
 int valor, total=0, contador;
 float media;
 
 contador=0;
 while (contador < MAXDIGIT){
    printf("Digite um valor inteiro positivo: ");
    scanf("%d",&valor);
    total += valor;
    contador++;
 }
 media = total / MAXDIGIT;
 printf("A media entre os %d valores digitados eh %.2f\n",MAXDIGIT, media);
 system ("pause");
}


Observe a utilização de uma constante inteira MAXDIGIT. Poderíamos usar diretamente o número 15, que é a quantidade de repetições que desejamos; no entanto, usar uma constante torna este código mais fácil de ser alterado. Se desejarmos mudar o valor de repetições de 15 para 10, por exemplo, só precisaríamos alterar a linha 5. Se tivéssemos usado o valor diretamente, teríamos que alterar as linhas 11, 17 e 18. Num código pequeno como este, isto não é tão vantajoso, mas imagine num código onde este valor aparecesse muitas outras vezes.
Dito isto, vamos ao que, no post de hoje, realmente nos interessa neste código: as linhas 10, 11 e 15.
Na linha 10 nós 'zeramos' uma variável inteira chamada contador. Esta variável poderia ter qualquer outro nome (o mais usado é i), mas escolhi este para deixar bem claro qual a função dela.
Para que o computador repita um bloco de comandos por um determinado número de vezes, ele deve saber quantas vezes este bloco já foi repetido para, assim, saber quando deve parar de repetí-lo. Por isto, o uso desta variável, que é chamada de variável contadora ou contador.
Na linha 15 esta variável é incrementada, depois que os comandos do bloco foram executados; ou seja, seria como se ele 'dissesse': fiz outra vez.
Na linha 11 é feita a verificação de quantas vezes a repetição já foi realizada. Se ainda não chegou ao número de vezes desejado, o bloco será repetido. Uma vez atingido o número de repetições desejado, o bloco é 'pulado' e o comando seguinte a ele é executado, neste caso, a linha 17 que calcula a média dos valores digitados.
A sintaxe do comando de repetição while com um número de repetições determinado é esta:

 contador = 0;
 while (contador < Número de repetições desejado){
    .
    .
    .
    contador++;
}


Existe uma outra forma de executar esta repetição no C. Utilizando um comando de repetição específico para uso com número de repetições determinado: o for.
A sintaxe do comando de repetições for é esta:

 for(contador = 0; contador < Número de repetições desejado; contador++){
    .
    .
    .
 }


Percebeu alguma coisa em comum com a sintaxe do comando de repetição while com um número de repetições determinado? Outra coisa que deve ser observada, são os ; dentro dos (). Eles indicam o fim de cada comando de controle da repetição.
Observe o mesmo código do exemplo anterior usando o comando de repetição for.


//PROGRAMA QUE CALCULA A MÉDIA ARITMÉTICA ENTRE 15 VALORES INTEIROS
#include <stdio.h>
#include <stdlib.h>
 
const int MAXDIGIT=15;
int main(){
 int valor, total=0, contador;
 float media;
 
 for(contador = 0; contador < MAXDIGIT; contador++){
    printf("Digite um valor inteiro positivo: ");
    scanf("%d",&valor);
    total += valor;
 }
 media = total / MAXDIGIT;
 printf("A media entre os %d valores digitados eh %.2f\n",MAXDIGIT, media);
 system ("pause");
}


Qual dos dois usar? Você decide! Uma grande maioria de programadores profissionais prefere esta 2ª versão, que utiliza o for.

Por hoje é só pessoal. Até a próxima.

quinta-feira, 5 de novembro de 2009

De novo! De novo!... Parte I (while/flag)

Hoje vamos começar a entender como funcionam os comandos de repetição.

Como fazer para que uma operação seja repetida?
Até agora vimos programas simples, em que são necessárias poucas interações do usuário. Pedimos que ele digite 2 ou 3 valores no máximo e, para isto, usamos um printf para informar o que ele deve digitar a cada vez. E se precisarmos que o usuário digite 30 valores? Já imaginou o tamanho do código? Quantas vezes será necessário digitar o mesmo trecho do printf? Não tem como fazer com que o computador execute essa repetição por nós? A resposta é SIM.
Antes de entender como fazer isto no C, vamos entender o que é uma repetição.

Repetir? O quê? E até quando?
Repetição é a execução de uma operação, que já foi realizada, outra vez.
Imagine que você tenha que colocar objetos dentro de uma caixa. Os passos para isto seriam:

abrir a caixa;
verificar se existem objetos para colocar na caixa;
se existir, pegar um objeto;
colocar o objeto dentro da caixa;
verificar se existem objetos para colocar na caixa;
se existir, pegar um objeto;
colocar o objeto dentro da caixa;
verificar se existem objetos para colocar na caixa;
se existir, pegar um objeto;
10º colocar o objeto dentro da caixa;
11º verificar se existem objetos para colocar na caixa;
12º se não existir;
13º fechar a caixa;

Repare que os 5º, 8º e 11º passos são idênticos ao 2º passo, os 6º e 8º passos são idênticos ao 3º passo, e o 7º e o 10º passos são idênticos ao 4º passo. Isto significa que as operações dos 2º, 3º e 4º passos se repetem. Pensando em termos de código fonte, logo vemos que os 2º, 3º e 4º passos fazem parte de um bloco. Já identificamos o o quê. Falta o até quando?
Olhando este roteiro acima, vemos que o nosso bloco se repete enquanto tiver objetos para colocar na caixa. Quando acabarem os objetos a serem colocados na caixa, não é mais necessário executar o nosso bloco.
Pronto! É assim que devemos pensar quando falarmos de repetição.
Um bloco de comandos que será repetido enquanto uma condição for verdadeira.
Neste caso, o bloco são os 2º, 3º e 4º passos, e a condição é que ainda hajam objetos para serem colocados na caixa. Se houverem 20 objetos, o bloco será repetido por 20 vezes, se houverem 5 objetos, será repetido por 5 vezes.
O comando C para isto é o while, e segue esta estrutura:

 while (condição){
            bloco de comandos
 }

Vamos criar um código fonte para exemplificar isto. Neste código vamos pedir que o usuário digite alguns valores, e vamos somar os valores digitados e exibir a soma deles quanto o usuário finalizar a digitação. Fazendo uma analogia com os objetos e a caixa, os objetos são os valores digitados e colocar esses objetos na caixa seria o mesmo que somá-los. Até aí está tranquilo, mas como vamos saber que 'acabaram os objetos'?

Quando se acena a bandeira, acaba a corrida
Para que o computador saiba quando o usuário finalizou a digitação de valores, alguém tem que informar isto a ele. Normalmente, quem deve fazer isto é o próprio usuário. Mas como ele vai fazer isto?
Você já viu uma corrida profissional de carros? Vários carros ficam dando voltas e voltas em uma pista de corridas. Essas corridas tem uma duração definida por quantas voltas serão realizadas, mas algumas vezes por causa de acidentes que podem acontecer, a quantidade de voltas pode ser reduzida. Quem define esta redução são os comissários de prova. O piloto do carro não tem idéia de que a corrida irá acabar antes do que foi determinado anteriormente. Para informar ao piloto que a corrida acabou é acenada um bandeira quadriculada. Ao ver esta bandeira quadriculada, o piloto sabe que é o fim da corrida.
O C utiliza o mesmo artifício. A isto se dá o nome de FLAG, que significa bandeira em inglês, e funciona assim:
Escolhemos um valor que, se for digitado, indicará que se deseja finalizar a entrada, e informamos isto ao usuário.
Geralmente este valor será definido como uma constante, já que será único e não será alterado durante a execução do programa.
No nosso exemplo usaremos o valor 0 (zero), já que somar 0 a um valor resultará no próprio valor.
Vamos ao código.


//EXEMPLOS DE COMANDO DE REPETIÇÃO (FLAG)
#include <stdio.h>
#include <stdlib.h>
 
const int SAIR=0;
int main(){
 int num, total=0;
 
 while (num != SAIR){
    printf("Digite um numero inteiro (0 (zero) para sair): ");
    scanf("%d",num);
    total += num;
 }
 printf("A soma dos valores informados eh igual a %d\n", total);
 system("pause");
}


Antes que você tente compilar este código, que parece estar correto, eu gostaria de informar que existe um erro grave nele. Sabe qual é?
Observe a linha 9. Nela está o nosso comando de repetição while e a condição que controlará a repetição, no caso enquanto o valor digitado for diferente do valor da constante inteira SAIR que é 0.
Como ainda não foi digitado nenhum valor, não se sabe qual o valor da variável num. Outra coisa a observar, é que se o usuário digitar 0, que é o valor da constante SAIR, a soma da linha 12 será executada, quando não deveria ser. Como escolhemos o valor 0 para ser o FLAG, esta soma não afetará o resultado final; mas se tivéssemos escolhido o valor -99, por exemplo, o resultado final estaria totalmente comprometido.
Sendo assim, temos que avaliar o valor digitado antes de efetuar a repetição.
Vejamos como ficaria este código, com esta avaliação antes da repetição, que é a forma correta.


//EXEMPLOS DE COMANDO DE REPETIÇÃO (FLAG)
#include <stdio.h>
#include <stdlib.h>
 
const int SAIR=0;
int main(){
 int num, total=0;
 
 printf("Digite um numero inteiro (0 (zero) para sair): ");
 scanf("%d",num);
 while (num != SAIR){
    total += num;
    printf("Digite um numero inteiro (0 (zero) para sair): ");
    scanf("%d",num);
 }
 printf("A soma dos valores informados eh igual a %d\n", total);
 system("pause");
}


Agora sim.
O valor digitado será armazenado na variável num (linha 10) antes de ser avaliado pelo comando de repetição while.
Se o usuário tiver digitado 0 que é o valor do FLAG, os comandos do bloco não serão repetidos.
Pode compilar e executar este código. Faça os seus testes, altere o valor do FLAG e verifique o que acontece.
É mais fácil do que parece.

E se quisermos repetir um bloco de comandos por um determinado número de vezes?
Veremos isto no próximo post.