Subseces
o
o o o
o o
1 Programas C++ 1.1 Sentenas: simples e compostas 1.2 Variveis em C++ 1.3 Definio de Varivel em C++ 1.4 Constantes 1.5 Caracteres Constantes 1.6 Entrada e Sada 1.6.1 Exibindo informaes na tela: cout 1.6.2 Lendo informao: cin 1.7 Algoritmo X Programa 2 Operaes Aritmticas e Expresses. Operaes Relacionais. 2.1 Operaes Aritmticas 2.1.1 Precedncia de Operadores 2.1.2 A Operao de Resto (%) 2.1.3 Expresses e Variveis 2.2 Operadores Relacionais 2.2.1 Precedncia dos operadores relacionais 2.3 Reviso de Expresses: 2.4 Exemplo de programas 2.5 Precedncia e associatividade de operadores 3 Expresses como valores 3.1 Expresses aritmticas, relacionais e lgicas 3.2 Expresses envolvendo o operador de atribuio (=) 4 Ordem sequencial de execuo de sentenas o comando condicional: if e if - else 4.1 Um erro comum 5 Aninhando senteas if e if-else 5.1 A ambigidade do else 6 Operadores Lgicos 7 Exemplos 7.1 IF - ELSE 7.2 Operadores lgicos 8 A construo else-if 9 Funes 9.1 Funes: o que so e por que us-las 9.2 Definindo funes 9.3 Funes simples 9.3.1 Argumentos 9.4 Funes que retornam um valor 9.5 Mais sobre o return 9.6 Mais sobre Argumentos 9.7 Chamada por valor 9.8 Variveis locais
9.9 Prottipos 9.10 Documentao de funes 9.11 Comentrios 10 Estruturas de Repetio 10.1 O comando de repetio while 10.2 Estilo de formatao para estruturas de repetio 10.2.1 Colocao das chaves 10.2.2 Necessidade ou no das chaves 10.2.3 Uso de espao em branco 10.2.4 Laos aninhados 11 Mais sobre funes: Quando return no suficiente 11.1 Usando referncia 11.2 Argumentos por referncia em funes 12 O pr-processador 12.1 A diretiva #define 12.2 A diretiva #include 12.3 Comentrios 13 Vetores ou Arrays 13.1 Definindo arrays e acessando seus elementos 13.2 Inicializao de arrays 13.3 Verificao de Limite 13.4 Arrays como argumentos de funes 13.5 Exemplo: pesquisa linear de um array 13.5.1 O Problema 13.6 Exemplo: somar os elementos de dois arrays 13.6.1 O Problema 13.7 Exemplo: Ordenao de um vetor - Verso 1 13.7.1 Prottipo da funo e definio 13.8 Exemplo: Ordenao de um vetor - Verso 2 13.8.1 Algoritmo Bubble Sort otimizado 13.8.2 Prottipo da funo e definio 13.9 Comentrios Finais 14 Matrizes ou Arrays Multidimensionais 14.1 Inicializao 14.2 Arrays Multidimensionais - arrays de arrays 14.3 Arrays Multidimensionais como argumento para funes
1 Programas C++
Essencialmente, um programa C++ consiste de uma ou mais partes chamadas funes1. Alm disso, um programa em C++ deve definir pelo menos uma funo chamada main. Esta funo marca o ponto de incio de execuo do programa. Programas C++ tem a seguinte estrutura geral:
#include
iostream
Simples:
x = 3;
Composta:
{ i = 3; cout << i << endl; i = i + 1; }
A figura acima mostra como um int e um char so armazenados na memria. Outro tipo existente o float, usado para armazenar nmeros reais (nmeros com o ponto decimal). Este nmeros so armazenados em duas partes: a mantissa e o expoente. Eles so armazenados de uma maneira que se assemelha a notao
escrito como
. Neste
Estes nmeros so armazenados de uma forma padro, tal que a mantissa tem apenas um dgito para a esquerda do ponto decimal. Desta forma, 3634.1 escrito como 3.6341e3, e 0.0000341 escrito 3.41e-5. Note tambm que a preciso limitada pela mantissa. Somente os 6 dgitos mais significativos so armazenados. Em Code::Blocks um float ocupa 4 bytes de memria. H muitos outros tipos ( short, long, double), que sero descritos no futuro.
qualquer sequncia de letras, digitos, e '_', MAS DEVE COMEAR com uma letra ou com '_'. Por exemplo, hora_inicio, tempo, var1 so nomes de variveis vlidos, enquanto 3horas, total$ e azul-claro no so nomes vlidos; Maisculas Minsculas; No so permitidos nomes ou palavras reservadas da linguagem.
break do for
volatile while
sempre uma boa idia ter certas regras (para voc mesmo) para nomear variveis para tornar o programa mais legvel:
D nomes significativos as variveis (mas no muito longos); Use nomes de variveis do tipo i, j, k somente para variveis tipo contadores; Pode-se usar letras maisculas ou '_' para juntar palavras. Por exemplo, horaInicio ou hora_inicio. Use o que voc preferir, mas SEJA CONSISTENTE em sua escolha.
8 8 32 32 64
-128 a 127 true ou false -2.147.483.647 a 2.147.483.647 7 dgitos significativos 15 dgitos significativos
Quando variveis so definidas, elas no possuem valores ainda. Ns damos valores s variveis usando o operador de atribuio (=). Variveis tambm podem ser inicializadas para conter valores quando so definidas. Usando esta forma, o program acima ficaria:
int main() { int pera = 3;
... }
um tipo: diz quantos bytes a varivel ocupa, e como ela deve ser interpretada. um nome: um identificador. um endereo: o endereo do byte menos significativo do local da memria associado a varivel. um valor: o contedo real dos bytes associados com a varivel; o valor da varivel depende do tipo da varivel; a definio da varivel no d valor a varivel; o valor dado pelo operador de atribuio, ou usando a funo cin. Ns veremos mais tarde que a funo cin atribui a uma varivel um valor digitado no teclado. Em C++ , nomes de variveis devem ser declarados antes de serem usados. Se no for declarado, ocorrer um erro de compilao. Devem ser dados valores s variveis antes que sejam utilizadas. Se voc tentar utilizar a varivel antes de especificar o seu valor, voc obter ``lixo'' (o que quer que esteja armazenado no endereo da varivel na memria quando o programa comea sua execuo), culminando com falha na execuo do programa.
1.4 Constantes
Em C++ , alm de variveis, ns podemos usar tambm nmeros ou caracteres cujos valores no mudam. Eles so chamados de constantes. Constantes no so associados a lugares na memria. Assim como variveis, constantes tambm tm tipos. Uma constante pode ser do tipo int, char, etc. Voc nao tem que declarar constantes, e pode utiliz-las diretamente (o compilador reconhece o tipo pela maneira que so escritos). Por exemplo, 2 do tipo int, e 2.0 do tipo double. Por conveno, todas as constantes reais so do tipo double.
que escrever '\\'. Note que \n o caracter de nova linha - embora use-se dois smbolos para represent-lo. A barra invertida chamada de escape. Ele diz ao compilador que o n que segue no a letra n, mas que a sequncia completa de caracteres deve ser interpretada como o caracter de ``nova linha''. Cada caracter constante tem um valor inteiro igual ao seu valor numrico do seu cdigo ASCII. Por exemplo, considere a constante 'A', que tem cdigo ASCII 65, e 'B' que tem cdigo 66. Ns podemos usar a expresso 'A' + 1. O resultado o valor 66. E se o tipo da expresso resultante for char, ento o resultado da expresso 'B'.
using namespace std; Isto faz com que o arquivo header chamado iostream seja includo no seu arquivo fonte durante a compilao. Este arquivo contm definies de diversas funes e classes (por exemplo, cout e cin). Ele declara ao compilador o nome das funes e algumas informaes adicionais necessrias para que as instrues sejam executadas corretamente.
cout
endl;
Imprimir Al todo mundo em uma linha na tela do computador. O valor endl representa a mudana de linha. Para o comando cout fazer o que deve, ns devemos especificar o que ser impresso. Ns devemos dar ao comando o que chamamos de argumentos. No exemplo acima, Al todo mundo e endl so argumentos para cout. Os argumentos de cout podem ser uma varivel, uma expresso ou um string (uma srie de caracteres entre aspas (")).
Ns tambm podemos colocar caracteres de escape no string para imprimir caracteres especiais. Por exemplo, colocando \n no string causa que o restante do string seja impresso na linha seguinte. Outros caracteres de escape sero apresentados no futuro. Considere o seguinte programa:
#include
iostream
int main()
int pera = 3;
cout "
pera
peso
" quilos."
endl;
cout
PRECO
peso * PRECO
endl;
A sada do programa ser: Existem 3 peras de qualidade A pesando 2.5 quilos. O preco por quilo eh 1.99, o total eh 4.975 A linha #define PRECO 1.99 no incio do programa define uma macro. Ou seja, definimos que PRECO um sinnimo para 1.99 e, portanto, toda ocorrncia de PRECO no programa substitudo por 1.99 antes que ele seja compilado.
#include
iostream
cout
cin
idade
cout }
idade
"anos."
endl;
Este programa mostrar no monitor: Entre sua idade: e aguardar que um nmero seja digitado e a tecla ENTER. Depois disso, a varivel idade conter o valor digitado pelo usurio. Mais de um valor pode ser lido por um mesmo cin. Considere o seguinte exemplo:
#include
iostream
cout
cin
dia
mes
ano;
Este exemplo funciona exatamente como o exemplo anterior. Um nico cin l os 3 nmeros quando estes nmeros so separados por espaos (espaos em branco, tabulao, novas linhas). Ento voc pode teclar ENTER depois de cada nmero, ou colocar espaos ou tabulaes entre os nmeros. Os espaos so ignorados pelo cin.
Programa em C++
/* programa que calcula o permetro e a rea de uma circunferncia de raio R (fornecido pelo usurio) */ #include <iostream> /* inclui diretivas de entrada-sada */ #include <cmath> /* inclui diretivas das funes matemticas */ using namespace std; #define PI 3.14159
int main( ) {
/* Definir variaveis */ int Raio; float Perim, Area; /* Obter Raio da circunferencia */ cout << "Entre com o valor do raio: "; cin >> Raio; /* Calcular Perimetro do Circulo */ Perim = 2 * PI * Raio; /* Calcular Area da Circunferencia */ Area = PI * pow(Raio, 2); /* Exibir Resultados */ cout << "O perimetro da circunferencia de raio " << Raio << " eh " << Perim << endl; cout << "e a area eh " << Area << endl; }
i = i + 1;
Assim, em , expresses em parntesis so avaliadas primeiro, seguidos por exponenciao, multiplicao, diviso, adio e subtrao. Da mesma forma, em C++ , expresses entre parntesis so executadas primeiro, seguidas de *, / e % (que tem todos a mesma precedncia), seguido de + e - (ambos com a mesma precedncia). Quando operaes adjacentes tm a mesma precedncia, elas so associadas da esquerda para a direita. Assim, a * b / c * d % e o mesmo que ((((a * b) / c) * d) % e).
Exemplos de lugares onde uma expresso aritmtica NO pode ser usada incluem:
int yucky + 2 = 5; cin >> oops * 5;
Os resultados deste operadores 0 (correspondendo a falso), ou 1 (correspondendo a verdadeiro). Valores como esses so chamados valores booleanos. Algumas linguagens de programao como Pascal tem um tipo de varivel distinto para valores booleanos. Este no o caso do C++ , onde valores booleanos so armazenados como variveis numricas tais como o int. Considere o seguinte programa:
int main() { int idade; idade = cout << << endl; idade = cout << << endl; } 17; "Pode tirar carteira de motorista? " << (idade >= 18) 35; "Pode tirar carteira de motorista? " << (idade >= 18)
Na primeira linha, idade 17. Logo, 17 >= 18 falso, que 0. Depois disso, idade 35. Logo, 35 >= 18 verdadeiro, que 1. Note tambm que o operador de igualdade escrito com ``sinais de igual duplo'', ==, no =. Tenha cuidado com esta diferena, j que colocar = no lugar de == no um erro sinttico (no gera erro de compilao), e no significa o que voc espera.
} #include <iostream> using namespace std; int main() { int n1, n2, n3; cout << "Entre com um numero inteiro: "; cin >> n1; n2 = n1 / 5; n3 = n2 % 5 * 7; cout << n2 << " " << n3 << " " << (n2 != n3 + 21) << endl; }
int main() { int numero; cout << "Entre com um numero inteiro: "; cin >> numero; cout << "\nPar? " << numero % 2 << endl; }
Exemplo 2: escreva um programa que leia 3 nmeros inteiros e calcule a soma, mdia, e produto.
#include <iostream> #include <iomanip> cout using namespace std; int main() { int n1, n2, n3; int soma; cout << "Entre com 3 numeros inteiros: "; cin >> n1 >> n2 >> n3; soma = n1 + n2 + n3; cout << "Soma = " << soma << endl; cout.setf (ios::fixed | ios::showpoint); // reais em ponto fixo cout.precision(2); // 2 casa decimais // setw(8) fixa tamanho da representao em 8 digitos cout << "Media = " << setw(8) << soma / 3.0 << endl; cout << "Produto = " << (unsigned) n1 * n2 * n3 << endl; } // necessario para usar setw() e setf() em
() -
esquerda para direita (unrios) direita para esquerda esquerda para direita esquerda para direita esquerda para direita esquerda para direita
tem valor 3; tem valor 1; tem valor igual ao valor da varivel x mais um; tem valor 1 quando o valor da varivel x fora do intervalo [1,4], e 0 quando x est dentro do intervalo.
x + 1
(x < 1) || (x > 4)
(1)
Um (do ingls ``left-hand-side value'' - valor a esquerda) um valor que se refere a um endereo na memria do computador. At agora, o nico ``lvalue'' vlido visto no curso o nome de uma varivel. A maneira que a atribuio funciona a seguinte: a expresso do lado direito avaliada, e o valor copiado para o endereo da memria associada ao ``lvalue''. O tipo do objeto do ``lvalue'' determina como o valor da armazenada na memria.
Expresses de atribuio, assim como expresses, tm valor. O valor de uma expresso de atribuio dado pelo valor da expresso do lado direito do =. Por exemplo:
x = 3 x = y+1
expresso y+1. Como consequncia do fato que atribuies serem expresses que so associadas da direita para esquerda, podemos escrever sentenas como:
i = j = k = 0;
Que, usando parnteses, equivalente a i = (j = (k = 0)). Ou seja, primeiro o valor 0 atribudo a k, o valor de k = 0 (que zero) atribudo a j e o valor de j = (k = 0) (que tambm zero) atribudo a i. Uma caracterstica muito peculiar de C++ que expresses de atribuio podem ser usados em qualquer lugar que um valor pode ser usado. Porm voc deve saber que uslo dentro de outros comandos produz um efeito colateral que alterar o valor da varivel na memria. Portanto, a execuo de:
int quadrado, n = 2; cout << "Quadrado de " << n << " eh menor que 50? " << ((quadrado = n * n) < 50) << endl;
causa no apenas que o valor 4 seja impresso, como a avaliao da expresso relacional dentro do cout faz com que o nmero 4 seja copiado para o endereo de memria associado com a varivel quadrado. Note que necessrio usar parnteses em quadrado = n * n j que = tem menor precedncia que o operador relacional <. Agora compare o exemplo anterior com o prximo, no qual o valor 4 impresso, mas sem nenhum efeito colateral:
int quadrado, n = 2; cout << "Quadrado de " << n << " eh menor que 50? " << (n * n < 50) << endl;
Note que agora no h necessidade de parnteses para a expresso n * n porque * tem maior precedncia que o operador relacional <.
if
O corpo do desvio, por sua vez, pode ser uma sentena simples ou composta (veja Seo 1.1). Quando uma sentena if encontrada em um programa, 1. O teste na em parnteses avaliada. 2. Se o valor da expresso de teste for DIFERENTE de zero, as sentenas que compem o corpo do desvio que segue a expresso de teste so executadas.
Figura 1: O comando if Considere o seguinte exemplo que converte uma frao digitada pelo usurio (numerador e denominador) em decimal e imprime o resultado:
#include <iostream> using namespace std; int main( ){ int a, b; cout << "Entre com uma cin >> a >> b; } fracao (numerador and denominador): ";
cout << "A fracao em decimal eh " << 1.0 * a / b << endl;
No exemplo acima, escrevemos 1.0 * a / b, j que a e b so do tipo int, e portanto a / b uma diviso de inteiros e a parte fracional do resultado seria truncado, o que certamente no o que desejamos. Voce v algo errado neste programa ? Uma coisa a ser notada que se o usurio digitar um denominador igual a 0, ns teremos um erro de execuo, j que o programa tentaria executar uma diviso por zero. O que necessrio fazer testar se o denominador igual a zero e dividir s no caso dele for diferente de zero. Poderamos reescrever o programa acima da seguinte forma: Exemplo 1:
#include <iostream> using namespace std; int main( ){ int a, b; cout << "Entre com uma cin >> a >> b; fracao (numerador e denominador): ";
if (b != 0) cout << "A fracao em decimal eh " << 1.0 * a / b << endl; }
Exemplo 2: Programa que l dois nmeros e ordena o par caso o primeiro nmero digitado for maior que o segundo.
#include <iostream> using namespace std; int main( ){ int num1, num2, aux; cout << "Entre com dois numeros inteiros: "; cin >> num1 >> num2; if (num1 > num2) { aux = num1; num1 = num2; num2 = aux; cout << "Trocou \n"; } cout << "Os numeros ordenados: " << num1 << " " << num2 << endl; }
O programa do Exemplo 1 acima ficaria ainda melhor se ao invs de no fazer nada no caso do denominador ser zero, imprimirmos uma mensagem de erro ao usurio, explicando o que h de errado.
if
else
Primeiro, a (que usualmente chamamos de condio) avaliada. Caso a condio seja verdadeira (o que equivalente a dizer que o valor diferente de zero), entao a executada. Caso contrrio, a executada.
Note que uma sentena pode ser simples ou composta. Se voc quiser agrupar diversas sentenas para serem executadas, voc pode coloc-las entre chaves ({ e }). Por hora, vamos continuar com nosso exemplo simples e torn-lo mais explicativo: Exemplo 3:
#include <iostream> using namespace std; int main( ){ int a, b;
cout << "Entre com uma fracao (numerador and denominador): "; cin >> a >> b; if (b != 0) cout << "A fracao decimal eh " << 1.0 * a / b << endl; else cout << "Erro: denominador zero!\n"; }
Exemplo 4: Considere agora o exemplo j visto que pede que um usurio entre com um nmero e verifique se o nmero par. Porm agora, queremos que o programa imprima ``o numero e par'' ou ``o numero e impar''.
#include <iostream> using namespace std; int main( ){ int num; // obtem um numero do usuario cout << "Entre com um inteiro: "; cin >> num; // imprime uma mensagem dizendo se o numero e par ou impar if (num % 2 == 0) cout << "O numero eh par.\n"; else cout << "O numero eh impar.\n"; }
if (saldo == 1) cout << "Voce esta quebrado! " << endl; else cout << "Seu saldo eh " << saldo << endl;
Como a sentena saldo = 2000 inicializa o valor da varivel saldo com 2000, a expresso saldo == 1 tem valor 0. Portanto, a sentea que segue o else ser executada, e a mensagem
Seu saldo e 2000
ser impressa. Agora, suponha que, devido a um erro, voc tenha colocado = ao invs de ==:
int saldo = 2000;
if (saldo = 1) cout << "Voce esta quebrado! " << endl; else cout << "Seu saldo eh " << saldo << endl;
Agora, a expresso saldo = 1 tem valor 1. Portanto, a sentena que segue o if ser executada, e a mensagem
Voce esta quebrado!
ser impressa. Alm disso, a atribuio causar um efeito colateral, e alterar o valor de saldo para 1. Tal uso do operador de atribuio no ilegal, e no ser detectado pelo compilador como erro. Portanto, tome cuidado com o uso de atribuio no lugar de igualdade. Tal erro muito comum, e no fcil de achar. Como regra geral, NO utilize atribuies dentro de outras sentenas.
O aninhamento de sentenas if-else sem usar chaves ({ e }) para delimitar o bloco de senteas a ser executado pode trazer efeitos indesejados. H uma regra simples para determinar qual if est associado a qual else. Regra de associao: Um else est associado com a ltima ocorrncia do if sem else. O exemplo seguinte est errado porque associa o else ao if "incorreto":
#include <iostream> using namespace std; int main( ){ int num; // Obtem um numero do usuario cout << "Entre com o numero de peras: "; cin >> num; // Imprime uma mensagem dizendo se o numero de peras e 0 ou 1 // (*** isto esta' errado !! ***) if (num != 0) if (num == 1) cout << "Voce tem uma pera.\n"; else cout << "Voce nao tem nenhuma pera.\n"; }
Para evitar este problema, chaves ({ e }) devem ser usadas para tirar a ambiguidade. O exemplo abaixo mostra como as chaves podem ser inseridas para corrigir o programa acima.
#include <iostream>
using namespace std; int main( ){ int num; // Obtem um numero do usuario cout << "Entre com o numero de peras: "; cin >> num; // Como corrigir o problema (este programa funciona) if (num != 0) { if (num == 1) cout << "Voce tem uma pera.\n"; } else cout << "Voce nao tem nenhuma pera.\n"; }
6 Operadores Lgicos
Todos os programas at agora consideraram if com condies de teste simples. Alguns exemplos de testes simples: b != 0, contador <= 5. Estas expresses testam uma condio. Portanto, quando mais de uma condio precisa ser testada, precisamos usar sentenas if e if-else aninhadas. A linguagem C++ , assim como a maioria das linguagens de programao de alto nvel suportam operadores lgicos que podem ser usados para criar operaes lgicas mais complexas, combinando condies simples. O valor de uma expresso lgica ou VERDADEIRO ou FALSO. Lembre que no h constantes lgicas VERDADEIRO e
FALSO em C++ ; em expresses lgicas 0 interpretado como FALSO, e qualquer valor diferente de zero interpretado como VERDADEIRO. Os operadores lgicos so
!
NO lgico, operao de negao (operador unrio) E lgico, conjuno (operador binrio) OU lgico, disjuno (operador binrio).
&& ||
Por exemplo, se quisermos testar se um nmero num positivo e par, e imprimir uma mensagem como no exemplo anterior, podemos escrever:
if (num >= 0) if (num % 2 == 0) cout << "Numero par nao negativo." << endl;
1 0
Os dois operadores binrios operam sobre duas expresses lgicas e tem o valor 1 (verdadeiro) or 0 (falso). Os exemplos abaixo mostram o seu uso:
a==0 && b==0 a==0 || b==0
(verdadeiro se ambos a == 0 e b == 0, portanto se a e b so 0) (verdadeiro se pelo menos uma das variveis a or b for 0)
Uma expresso usando && verdadeira somente se ambos os operadores forem verdadeiros (no zero). Uma expresso usando || falsa somente se ambos os operadores forem falsos (zero). Verifique na Tabela 2 o resultado do uso de operadores lgicos:
A precedncia do operador de negao lgica a mais alta (no mesmo nvel que o ``-'' unrio). A precedncia dos operadores lgicos binrios menor que a dos operadores relacionais, e mais alta que a operao de atribuio. O && tem precedncia mais alta que o ||, e ambos associam da esquerda para a direita (como os operadores aritmticos). Como a precedncia dos operadores lgicos menor que a dos operadores relacionais, no necessrio usar parnteses em expresses como:
x >= 3 && x <= 50 x == 1 || x == 2 || x == 3
() ! -
esquerda para direita (unrios) direita para esquerda esquerda para direita esquerda para direita esquerda para direita esquerda para direita esquerda para direita esquerda para direita
No prximo exemplo, o programa verifica se as trs variveis lado1, lado2, e lado3, podem ser lados de um tringulo reto. Ns usamos o fato que os trs valores devem ser positivos, e que o quadrado de um dos lados deve ser igual a soma dos quadrados dos outros lados (Teorema de Pitgoras) para determinar se o tringulo reto.
#include <iostream> using namespace std; int main( ){ int lado1, lado2, lado3; int s1, s2, s3; cout << "Entre com o tamanho dos lados do triangulo: "; cin >> lado1 >> lado2 >> lado3; // s1 s2 s3 calcula o quadrado dos lados = lado1*lado1; = lado2*lado2; = lado3*lado3;
// testa a condicao para um triangulo reto if ( lado1>0 && lado2>0 && lado3 > 0 ) { if (s1==s2+s3 || s2==s1+s2 || s2==s1+s3) ) { cout << "Triangulo reto!\n"; } else { cout << "Nao pode ser um triangulo!\n"; } }
Na utilizao de expresses lgicas, as seguintes identidades so teis. Elas so chamadas de Lei de DeMorgan:
!(x && y)
equivalente a !x || !y
e
!(x || y)
equivalente a !x && !y
7 Exemplos
7.1 IF - ELSE
Assuma as seguintes declaraoes de variveis:
int x = 4; int y = 8;
9.
==> a c d
13. altere o programa acima para produzir a seguinte saida: o Assuma x = 5 e y = 8 1. a 2. a d o Assuma x = 5 e y = 7 a. b c d
8 A construo else-if
Embora ela no seja um tipo diferente de sentena, a seguinte construo bastante comum para programar decises entre diversas alternativas:
if
else if
else if
else if
else
As expresses lgicas so avaliadas em ordem, comeando com a . Se uma das expresses for verdadeira, a sentena associada ser executada. Se nenhuma for verdadeira, ento a sentena, , do ltimo else ser executada como opo default. Se a opo default no for necessria, ento a parte
else
Exemplo 9: O seguinte exemplo mostra um else-if de trs opes. O programa l dois nmeros e diz se eles so iguais ou se o primeiro nmero menor ou maior que o segundo.
#include <iostream> using namespace std; int main( ){ int num1, num2; // obtem 2 numeros do usuario cout << "Entre um numero: "; cin >> num1; cout << "Entre com um outro numero: "; cin >> num2; // mostra a mensagem de comparacao if (num1 == num2) cout << "Os numeros sao iguais\n"; else if (num1 < num2) cout << "O primeiro numero e menor\n"; else cout << "O primeiro numero e maior\n"; }
No programa acima, se (num1 == num2) for verdadeiro, ento os nmeros so iguais. Seno, verificado se (num1 < num2). Se esta condio for verdadeira, ento o primeiro nmero menor. Se isso no for verdadeiro, ento a nica opo restante que o primeiro nmero maior. Exemplo 10: Este programa l um nmero, um operador e um segundo nmero e realiza a operao correspondente entre os operandos dados.
#include <iostream> using namespace std; int main( ){
float num1, num2; char op; // obtem uma expressao do usuario cout << "Entre com numero operador numero\n"; cin >> num1 >> op >> num2; // mostra o resultado da operacao if (op == '+') cout << " = " << setprecision(2) else if (op == '-') cout << " = " << setprecision(2) else if (op == '/') cout << " = " << setprecision(2) else if (op == '*') cout << " = " << setprecision(2) else cout << " Operador invalido."; cout << endl; }
<< num1 + num2; << num1 - num2; << num1 / num2; << num1 * num2;
9 Funes
9.1 Funes: o que so e por que us-las
Quando queremos resolver um problema, em geral tentamos dividi-lo em subproblemas mais simples e relativamente independentes, e resolvemos os problemas mais simples um a um. A linguagem C++ dispe de construes (abstraes) que auxiliam o projeto de programas de maneira top-down. Uma funo cria uma maneira conveniente de encapsular alguns detalhes de ``processamento'', ou seja, como algum resultado obtido. Quando esta ``computao'' necessria, a funo chamada, ou invocada. Desta forma, quando uma funo chamada o usurio no precisa se preocupar como a computao realizada. importante saber o que a funo faz (qual o resultado da execuo de uma funo) e tambm como se usa a funo. Criando funes, um programa C++ pode ser estruturado em partes relativamente independentes que correspondem as subdivises do problema. Voc j viu algumas funes: cin.get(), sqrt(). Elas so funes de uma biblioteca padro (do C++ ). Voc no sabe como elas foram escritas, mas j viu como utiliz-las.
Ou seja, voc sabe o nome das funes e quais informaes especficas voc deve fornecer a elas (valores que devem ser passados para as funes) para que a funo produza os resultados esperados. Quando nos referirmos a uma funo neste texto usaremos a maneira frequentemente utilizada que o nome da funo seguido de (). Tomemos como exemplo o programa abaixo, que recebe 2 conjuntos de 3 nmeros e soma o maior valor de cada conjunto:
/* --------------------------------------------------------------------Recebe 2 conjuntos de 3 nmeros e soma o maior valor de cada conjunto. --------------------------------------------------------------------*/ #include <iostream> #include <cmath> using namespace std; int main() { int a, b, c, maior_1, maior_2; /* L o primeiro conjunto de 3 valores */ cin >> a >> b >> c; /* Verifica o maior dos trs valores informados */ /* Assume inicialmente que a varivel 'a' tem o maior valor */ maior_1 = a; /* Somente muda o valor de 'maior' se os valores em 'b' ou 'c' forem maiores */ if (b > maior_1) { maior_1 = b; } if (c > maior_1) { maior_1 = c; } /* Neste ponto do programa, maior_1 contm o maior valor dentre os 3 primeiros valores informados */ /* L o segundo conjunto de 3 valores */ cin >> a >> b >> c; /* Verifica o maior dos trs valores informados */ /* Algoritmo igual ao acima, exceto pela varivel que recebe o maior valor */ /* Assume inicialmente que a varivel 'a' tem o maior valor */
maior_2 = a; /* Somente muda o valor de 'maior' se os valores em 'b' ou 'c' forem maiores */ if (b > maior_2) { maior_2 = b; } if (c > maior_2) { maior_2 = c; } /* Neste ponto do programa, maior_2 contm o maior valor dentre os 3 valores informados no 2o. conjunto de entrada */ /* Calcula e exibe a soma solicitada */ cout << endl << maior_1 << " + " << maior_2 << " = " << maior_1 + maior_2 << endl; }
Observe que o cdigo que verifica o maior valor dentre 3 nmeros teve que ser reproduzido dentro do programa por duas vezes (para descobrir o maior valor de dois conjuntos diferentes de 3 nmeros). Um dos benefcios mais bvios de usar funes que podemos evitar repetio de cdigo. Em outras palavras, se voc quiser executar uma operao mais de uma vez, voc pode simplesmente escrever a funo uma vez e utiliz-la diversas vezes ao invs de escrever o mesmo cdigo vrias vezes. Outro benefcio que se voc desejar alterar ou corrigir alguma coisa mais tarde, mais fcil alterar em um nico lugar. O exemplo acima poderia ser simplificado pela criao de uma funo chamada maior, que dados trs nmeros , , e , d como resultado o maior valor dentre os trs valores fornecidos:
int maior (int a, int b, int c) { int maior; /* Assume inicialmente que a varivel 'a' tem o maior valor */ maior = a; /* Somente muda o valor de 'maior' se os valores em 'b' ou 'c' forem maiores */ if (b > maior) { maior = b; } if (c > maior) { maior = c; }
return maior; }
O exemplo pode ser ento alterado e simplificado com o uso da funo maior():
/* --------------------------------------------------------------------Recebe 2 conjuntos de 3 nmeros e soma o maior valor de cada conjunto. --------------------------------------------------------------------*/ #include <iostream> #include <cmath> using namespace std; /* Funo que retorna o maior valor entre x, y e z */ int acheMaior (int x, int y, int z) { int maior; /* Assume inicialmente que a varivel 'x' tem o maior valor */ maior = x; /* Somente muda o valor de 'maior' se os valores em 'y' ou 'z' forem maiores */ if (y > maior) { maior = y; } if (z > maior) { maior = z; } return maior; } int main() { int a, b, c, maior_1, maior_2; /* L o primeiro conjunto de 3 valores */ cin >> a >> b >> c; /* Verifica o maior dos trs valores informados */ maior_1 = acheMaior(a,b,c); /* Neste ponto do programa, maior_1 contm o maior valor dentre os 3 primeiros valores informados */
/* L o segundo conjunto de 3 valores */ cin >> a >> b >> c; /* Verifica o maior dos trs valores informados */ /* Usa a mesma funo, pois o procedimento para encontrar maior valor de 3 nmeros o mesmo. */ maior_2 = acheMaior(a,b,c); /* Neste ponto do programa, maior_2 contm o maior valor dentre os 3 valores informados no 2o. conjunto de entrada */ /* Calcula e exibe a soma solicitada */ cout << endl << maior_1 << " + " << maior_2 << " = " << maior_1 + maior_2 << endl; } o
Como pode ser observado, sejam quais forem os conjuntos de 3 nmeros fornecidos, no precisa escrever um cdigo similar ao mostrado na funo maior acima para cada nmero. Basta chamar a funo maior(), passar os valores necessrios para verificar o maior valor de cada conjunto, e utilizar os resultados. Evitar repetio de cdigo a razo histrica que funes foram inventadas (tambm chamado de procedimento ou subrotinas em outras linguagens de programao). A maior motivao para utilizar funes nas linguagens contemporneas a reduo da complexidade do programa e melhoria da modularidade do programa. Dividindo o programa em funes, muito mais fcil projetar, entender e modificar um programa. Por exemplo, obter a entrada do programa, realizar as computaes necessrias e apresentar o resultado ao usurio pode ser implementado como diferentes funes chamadas por main() nesta ordem. Funes podem ser escritas independentemente uma da outra. Isto significa que, em geral, variveis usadas dentro de funes no so compartilhadas pelas outras funes. Assim sendo, o comportamento da funo previsvel. Se no for assim, duas funes completamente no relacionadas podem alterar os dados uma da outra. Se as variveis so locais a uma funo, programas grandes passam a ser mais fceis de serem escritos. A comunicao entre funes passa a ser controlada - elas se comunicam somente atravs pelos valores passados as funes e os valores retornados.
using namespace std; // definicao da funcao alo() void alo(void) { cout << "Alo!" << endl; } // definicao da funcao main() int main () { int i; i = 1; while (i <= 3) { alo(); i = i + 1; } }
Todas as funes devem ser declaradas ou definidas antes de serem usadas. As funes da biblioteca padro, tais como cin.get(), so pr-definidas, mas mesmo assim devem ser declaradas (deve ser anunciado ao compilador que elas existem). por isso que inclumos a linha #include <iostream> no incio do cdigo fonte. O formato geral da definio de uma funo tipo-do-resultado nome-da funo (lista-de-argumentos)
{
declaraes e sentenas
}
A primeira linha da definio o cabealho da funo. Ela tm trs partes principais: o nome da funo, o tipo do resultado (que um valor) que a funo computa e retorna, e entre parnteses uma lista de parmetros (tambm chamado de argumentos formais). Se a funo no retorna nenhum valor, o tipo chamado de void, e esta palavra escrita no cabealho na frente do nome da funo. Se a funo no tiver argumentos formais, a palavra void pode ser escrita no lugar da lista de argumentos formais entre os parnteses. Para simplificar a exposio, falaremos sobre o tipo do retorno e os argumentos formais mais tarde. Eles servem para permitir que as funes troquem informaes entre si.
nome-da-funo(void)
O primeiro void significa que esta funo no tem tipo de retorno (no retorna um valor), e o segundo significa que a funo no tem argumentos (ela no precisa de nenhuma informao externa para ser executada). Isso no significa que a funo no faz nada. Ela pode realizar alguma ao, como imprimir uma mensagem. O exemplo abaixo mostra um programa que usa uma funo como essa:
#include <iostream> using namespace std; // DEFINIO da funo alo() void alo(void) { cout << "Alo." << endl; } // Programa Principal int main() { alo(); }
Neste exemplo, o programa consiste de duas funes, main() e alo(). A funo alo() imprime a mensagem Alo. quando chamada. A sentena cout o corpo da funo. Dentro da funo main() h uma chamada a funo alo(). A funo chamada pelo seu nome seguido de () (j que a funo alo no tem argumentos, nenhuma expresso escrita dentro dos parnteses). A funo alo() no retorna um valor, ela chamada simplesmente para realizar uma ao (imprimir a mensagem). A chamada de funo uma sentena vlida em C++ , portanto deve ser terminada por ponto e vrgula (;).
alo();
Observe que a ordem em que as funes so definidas dentro do cdigo-fonte importante, sendo que uma funo deve sempre ser definida ANTES das funes em que ela CHAMADA. No nosso exemplo, como a funo alo() chamada pela funo main(), ento a DEFINIO da funo alo() deve vir antes da definio da funo main(). O uso de prottipos pode ser usado para definir as funes em qualquer ordem dentro do cdigo-fonte, o que ser visto na Seo 9.9. Outra coisa que voc deve ter notado que main() tambm uma funo. A funo main() no difere em nada das demais funes, com a exceo de que contm o programa principal, isto , ao se executar um programa, ela a primeira funo a ser executada. As demais funes so executadas somente quando chamadas a partir da execuo da funo main().
9.3.1 Argumentos
Nosso prximo exemplo pede que o usurio digite suas iniciais, e ento chama a funo cumprimenta() para imprimir a mensagem ``Ola'' junto com as iniciais digitadas. Estas iniciais (seus valores) so passadas para a funo cumprimenta(). A funo cumprimenta() definida de forma que ela imprimir a mensagem incluindo quaisquer iniciais passadas.
#include <iostream> using namespace std; void cumprimenta(char inic1, char inic2) { cout << "Ola, " << inic1 << inic2 << "!" << endl; } int main() { char primeiro, segundo; cout << "Entre com duas iniciais (sem separacao): "; cin >> primeiro >> segundo ; cumprimenta(primeiro, segundo); }
A funo main() chama a funo cumprimenta(). Ao fazer esta chamada, main() passa para cumprimenta() os valores dos dois caracteres para serem impressos. Veja um exemplo de execuo do programa:
Entre com duas iniciais (sem separacao): YK Alo, YK!
Note que h uma correspondncia entre a quantidade, a ordem e tipo dos valores que main() passa (estes so chamados de parmetros reais ou argumentos reais) e os argumentos listados no cabealho da funo cumprimenta() (denominados argumentos formais).
expresso;
A expresso avaliada e o seu valor convertido ao tipo de retorno da funo (o tipo da funo dado no cabealho da funo antes do nome da funo).
Considere o seguinte exemplo. O programa consiste de duas funes: main() e quadrado. O programa pede que o usurio digite trs nmeros e verifica se eles podem ser os lados de um tringulo reto.
// programa que verifica se 3 numeros podem ser os lados de um // triangulo reto. // #include <iostream> using namespace std; // funcao que calcula o quadrado de um numero int quadrado(int n) { return n * n; } int main() { int s1, s2, s3; cout << "Entre tres inteiros: "; cin >> s1 >> s2 >> s3; if ( s1 > 0 && s2 > 0 && s3 > 0 && (quadrado(s1) + quadrado(s2) == quadrado(s3) || quadrado(s2) + quadrado(s3) == quadrado(s1) || quadrado(s3) + quadrado(s1) == quadrado(s2)) ) { cout << " " << s1 << " " << s2 << " " << s3 << " podem formar um triangulo reto\n"; } else { cout << " " << s1 << " " << s2 << " " << s3 << " nao podem formar um triangulo reto\n"; } }
Note que quando chamamos a funo quadrado() passamos o valor no qual desejamos executar o clculo, e tambm usamos o valor retornado pela funo em expresses. O valor de quadrado(s1) o valor que a funo quadrado() retorna quando chamado com o valor do argumento sendo igual ao valor da varivel s1. Os valores retornados pelas chamadas de funes podem ser usados em todos os lugares onde valores podem ser usados. Por exemplo,
y = quadrado(3); Aqui quadrado(3) tem
x = quadrado(3) + quadrado(4); atribuir 25 a varivel x, e area = quadrado(tamanho); atribuir a varivel area o valor
#include <iostream> using namespace std; int cinco(void) { return 5; } int main() { cout << "cinco = " << cinco() << endl; }
porque o valor de cinco() dentro da sentena cout 5. Olhando na sentena return, 5 a expresso retornada para o chamador. Outro exemplo:
#include <iostream> using namespace std; int obtem_valor(void) { int valor; cout << "Entre um valor: "; cin >> valor; return valor; } int main() { int a, b; a = obtem_valor(); b = obtem_valor(); cout << "soma = " << a + b << endl; }
Este programa obtm dois inteiros do usurio e mostra a sua soma. Ele usa a funo obtem valor() que mostra uma mensagem e obtm o valor do usurio. Um exemplo de sada deste programa :
Entre um valor: 15 Entre um valor: 4 soma = 19
Quando uma funo return executada, a funo imediatamente acaba - mesmo que haja cdigo na funo aps a sentena return. A execuo do programa continua aps o ponto no qual a chamada de funo foi feita. Sentenas return podem ocorrer em qualquer lugar na funo - no somente no final. Tambm vlido ter mais de um return dentro de uma funo. A nica limitao que return retorna um nico valor. O seguinte exemplo mostra uma funo (uma verso para int da funo obtem valor) que pede para usurio um valor e se o usurio digitar um valor negativo, imprime uma mensagem e retorna um valor positivo.
int obtem_valor_positivo(void) { int valor; cout << "Entre um valor: "; cin >> valor; if (valor >= 0) return valor; cout << "Tornando o valor positivo..." << endl; return -valor; }
Em uma funo void, return; (s com ;) pode ser usado para sair de uma funo. O exemplo seguinte, pede instrues ao usurio. Se o usurio reponder nao, a funo termina. Do contrrio, ele imprime as instrues e depois termina.
void instrucoes(void) { int ch; cout << "Voce quer instrucos? (s/n): "; ch = cin.get(); /* Termina se resposta for n */ if (ch == 'n' || ch == 'N') return; /* Mostra instrucoes */ cout << "As regras do jogo sao . . . "; . . . return; }
O return final (antes de fechar as chaves do corpo da funo) na funo opcional. Se omitido, a funo atingir o final da funo e retornar automaticamente. Note que o return opcional somente para funes void.
A comunicao entre uma funo e o chamador pode ser nas duas direes. Argumentos podem ser usados pelo chamador para passar dados para a funo. A lista de argumentos definida pelo cabealho da funo entre parnteses.. Para cada argumento voc precisa especificar o tipo do argumento e o nome do argumento. Se houver mais de um argumento, eles so separados por vrgula. Funes que no possuem argumentos tem void como lista de argumento. No corpo da funo os argumentos (tambm chamados de argumentos formais ou parmetros formais) so tratados como variveis. erro defini-los dentro do corpo da funo porque eles j esto definidos no cabealho. Antes da execuo da funo os valores passados pelo chamador so atribudos aos argumentos da funo. Considere o seguinte programa com a funo abs() que calcula o valor absoluto de um nmero.
#include <iostream> using namespace std; /* Definicao da funcao abs */ int abs(int x) { if (x < 0) x = -x; return x; } int main() { int n; cout << "Entre um numero: "; cin >> n; cout << "Valor absoluto de " << n << " eh " << abs(n) << endl; }
A funo abs() tem um argumento do tipo int, e seu nome x. Dentro da funo, x usado como uma varivel x. Uma vez que abs() tem um nico argumento, quando ela chamada, h sempre um valor dentro do parnteses, como em abs(n). O valor de n passado para a funo abs(), e antes da execuo da funo, o valor de n atribudo a x. Aqui est um exemplo de uma funo que converte uma temperatura de Farenheit para Celsius:
float fahr_para_cels(float f) { return 5.0 / 9.0 * (f - 32.0); }
Como voc pode ver, esta funo tem somente um argumento do tipo float. Um exemplo de chamada desta funo poderia ser:
fervura = fahr_para_cels(212.0);
O resultado da funo fahr para cels(212.0) atribudo a fervura. Portanto, depois da execuo desta sentena, o valor de fervura (que do tipo float) ser 100.0. O exemplo seguinte possui mais de um argumento:
float area(float largura, float altura) { return largura * altura; }
Esta funo possui dois argumentos do tipo float. Para chamar uma funo com mais de um argumento, os argumentos devem ser separados por vrgula. A ordem em que os argumentos so passados deve ser na mesma em que so definidos. Neste exemplo, o primeiro valor passado ser a largura e o segundo a altura. Um exemplo de chamada seria
tamanho = area(14.0, 21.5);
Depois desta sentena, o valor de tamanho (que do tipo float) ser 301.0. Quando passar os argumentos, importante ter certeza de pass-los na ordem correta e que eles so do tipo correto. Se isso no for observado, pode ocorrer erro ou aviso de compilao, ou resultados incorretos podem ser gerados. Uma ltima observao. Os argumentos que so passados pelo chamador podem ser expresses em geral e no somente constantes e varivies. Quando a funo chamada durante a execuo do programa, estas expresses so avaliadas, e o valor resultante passado para a funo chamada.
somente o valor (no o endereo) de x passado para quadrado. Por exemplo, se a varivel tem valor 5, para a funo quadrado(), quadrado(x) ou quadrado(5) so o mesmo. De qualquer forma, quadrado() receber somente o valor 5. quadrado() no sabe se na chamada da funo o 5 era uma constante inteira, o valor de uma varivel do tipon int, ou alguma expresso como 625/25 - 4 * 5. Quando quadrado() chamado, no interessa qual a expresso entre parnteses, ela ser avaliada e o valor passado para quadrado(). Esta maneira de passar argumentos chamada de chamada por valor. Argumentos em C++ so passados por valor. Portanto, a funo chamada no pode alterar o valor da varivel passada pelo chamador como argumento, porque ela no sabe em que endereo de memria o valor da varivel est armazenado.
A funo main() usou um nome x, mas x no definido dentro de main; ele uma varivel local a get int(), no a main(). Este programa gera erro de compilao. Note que possvel ter duas funes que usam variveis locais com o mesmo nome. Cada uma delas restrita a funo que a define e no h conflito. Analise o seguinte programa (ele est correto):
#include <iostream> using namespace std; int obtem_novo_int(void) { int x; cout << "Entre um valor: "; cin >> x; cout << "Obrigado!\n"; return x; } int main() { int x; x = obtem_novo_int();
/* ****Isto nao esta errado !! **** */ cout << "Voce digitou " << x << endl; }
A funo obtem novo int() usa uma varivel local chamada x para armazenar o valor digitado e retorna como resultado o valor de x. main() usa outra varivel local, tambm chamada de x para receber o resultado retornado por obtem novo int(). Cada funo tem sua prpria varivel x.
9.9 Prottipos
Os prottipos servem para dar ao compilador informaes sobre as funes. Isso para que voc possa chamar funes antes que o compilador tenha a definio (completa) das funes. O prottipo de uma funo idntico ao cabealho da funo, mas o nome dos argumentos podem ser omitidos e ele terminado com um ponto e vrgula. Prottipos declaram uma funo ao invs de defini-las. O formato de um prottipo : tipo-de-retorno nome-da-funo(lista-dos-tipos-dos-argumentos); Definindo prottipos, voc no precisa se preocupar com a ordem em que define as funes dentro do cdigo-fonte do programa. A principal vantagem de definir prottipos que erros de chamada de funes (como chamar uma funo com o nmero incorreto de argumentos, ou com argumentos de tipo errado) so detectados pelo compilador. Sem prottipos, o compilador s saberia que h erro depois de encontrar a definio da funo. Abaixo, mostramos a definio de duas funes e seus respectivos prottipos:
float volume(float, float, float); float dinheiro(int, int, int, int); float volume(float comprimento, float largura, float altura) { return comprimento * largura * altura; } float dinheiro(int c25, int c10, int c5, int c1) { return c25 * 0.25 + c10 * 0.10 + c5 * 0.05 + c1 * 0.01; }
Entrada - descrio dos argumentos passados para a funo Sada - descrio do valor retornado pela funo Suposies - o que voc assume ser verdade para que a funo funcione apropriadamente Algoritmo - como o problema resolvido (mtodo) Estas informaes devem ser colocadas como comentrio antes da definio da funo.
9.11 Comentrios
Voc pode colocar comentrios no seu programa para documentar o que est fazendo. O compilador ignora completamente o que quer esteja dentro de um comentrio. Comentrios em C++ so textos que comeam com // em cada linha, ou so delimitados por /* e */. Os smbolos // no possuem espao entre si. Alguns exemplos:
// Este um comentrio sem graa // Este um comentrio um pouco // maior que tem diversas linhas /* Este um outro comentrio maior ainda e que tem vrias linhas explicando qualquer aspecto mais detalhado do programa */ Regras para comentrio
sempre uma boa idia colocar comentrios em seu programa das coisas que no so claras. Isto vai ajudar quando mais tarde voc olhar o programa que escreveu j h algum tempo ou vai ajudar a entender programas escritos por outra pessoa. Um exemplo de comentrio til:
/* converte temperatura de farenheit para celsius */ celsius = (fahrenheit - 32) * 5.0 / 9.0;
o comentrio basicamente uma transcrio do cdigo do programa. Em seu lugar, um comentrio como
seria mais informativo neste ponto. Em outras palavras, voc deve comentar o cdigo, e no codificar o comentrio. Voc tambm deve evitar comentrios inteis ou bvios. Por exemplo:
// Incrementa i i = i + 1;
No h necessidade de comentrios j que i = i + 1 j auto explicativo. Abaixo est um exemplo de como voc deve comentar uma funo.
/* funo instrucoes() * acao: mostra instrucoes do programa * entrada: nenhuma * saida: nenhuma * suposicoes: nenhuma * algoritmo: imprime as instrucoes */ void instrucoes(void) { /* mostra instrucoes */ cout << "O processo de purificacao do Uranio-235 e' . . . . . }
";
10 Estruturas de Repetio
A linguagem C++ possui comandos para repetir uma sequncia de instrues. Estas estruturas de repetio, tambm conhecidas como laos (do ingls loops). Nesta seo veremos a estrutura while, Sendo que as demais estruturas de repetio em C++ , for e do ... while sero vistas na Seo 19.
executado. Depois desta execuo, o processo repetido a partir da expresso teste. O corpo do lao, por sua vez, pode ser uma sentena simples ou composta (veja Seo 1.1).
Sada:
contador = 0 contador = 1 contador = 2 contador = 3 contador = 4 ACABOU !!!!
Neste exemplo, a expresso de teste contador < 5, e o corpo do lao a sentena cout. Se examinarmos cuidadosamente este exemplo, veremos que a varivel contador inicializada com 0 (zero). Depois disso, a expresso de teste verificada e, como 0 < 5 verdadeiro, o corpo da repetio executado. Assim, o programa imprime contador = 0, e incrementa contador. Em seguida, a expresso de teste verificada novamente e todo o processo se repete at que contador seja 4 e contador = 4 seja impresso.
Depois disso, contador incrementado para 5 e o teste executado. Mas desta vez, 5 < 5 falso, ento a repetio no continua. A execuo do programa continua na sentena que segue o lao (no caso, imprimir a frase ACABOU !!!). Imediatamente aps a execuo do while, a varivel contador tem valor 5. O exemplo seguinte mostra um uso mais apropriado do comando while: Em situaes onde o nmero de repeties no conhecido antes do inico do comando while. Exemplo 1: Este programa pede nmeros ao usurio at que a soma de todos os nmeros digitados for pelo menos 20.
#include <iostream> using namespace std; int main( ){ int total, num; total = 0; while( total < 20 ) { cout << "Total = " << total << endl; cout << "Entre com um numero: "; cin >> num; total = total + num; } cout << "Final total = " << total << endl; }
Exemplo de sada:
Total Entre Total Entre Total Entre Final = 0 com um numero: 3 = 3 com um numero: 8 = 11 com um numero: 15 total = 26
Inicialmente, dado o valor 0 varivel total, e o teste verdadeiro ( 0 < 20). Em cada iterao, o total impresso e o usurio digita um nmero que somado a total. Quanto total for maior ou igual a 20, o teste do while torna-se falso, e a repetio termina.
APENAS UM DESTES ESTILOS deve ser consistentemente usado para as sentenas de repetio ( for, while e do ... while). Use o estilo com o qual voc se sentir mais confortvel.
Embora as chaves possam ser omitidas, h uma nica razo para coloc-las sempre. Considere o caso simples abaixo:
while( i < 5 ) { i = i + 1; }
Quando voc adicionar algo ao programa, voc poder adicionar uma sentena para um lao com apenas uma sentena. Se voc fizer isso, vital que voc tambm adicione chaves. Se voc no fizer isso, a segunda sentena do lao no ser considerada como parte do lao. Por exemplo:
while( i < 5 ) i = i + 1; j = j + 1;
ou
while (i<5)
ou
while( i < 5 )
Isto tambm uma escolha pessoal. Porm seja consistente em sua escolha !
Sada:
1 2 3 4 2 3 4 4 6 8 6 9 12 8 12 16
No exemplo acima, para cada iterao do lao externo, o lao interno imprime uma linha com nmeros e depois pula de linha. Exemplo 3: Este exemplo parecido com o anterior, exceto que o cout que produz a mudana de final de linha colocado dentro do lao interno. Como era de se esperar uma nova linha impressa aps cada valor ao invs de ser depois de 4 valores.
#include <iostream> #include <iomanip> // Necessrio para se usar a funo setw() em cout using namespace std; int main( ){ int linha, coluna; linha = 1; while (linha < 5) { coluna = 1; while (coluna < 5) { cout << setw(3) << linha * coluna; cout << endl; coluna = contador + 1; } linha = linha + 1; } }
Sada:
1 2 3 4 2 4 6 8 3 6 9 12 4 8 12 16
Exemplo 4: Este exemplo imprime um tringulo de asteriscos, de forma que a quantidade de asteriscos em uma linha igual ordem da linha (na linha 1, 1 asterisco, na linha 2, 2 asteriscos, etc.)
#include <iostream> using namespace std; int main( ){ int linha, coluna; cout << endl; linha = 1; while (linha < 8) { cout << "\t"; coluna = 1; while (coluna < linha) { cout << "*"; coluna = coluna + 1; } cout << endl; linha = linha + 1; } }
Sada:
* ** *** **** ***** ****** ******* ********
temp = a; a = b; b = temp; cout << "Trocados, eles sao " << a << " e " << b << endl; }
possvel escrever uma funo que executa esta operao de troca? Considere a tentativa abaixo de escrever esta funo:
#include <iostream> using namespace std; void troca(int x, int y) { int temp; temp = x; x = y; y = temp; } int main() { int a, b; cout << "Entre dois numeros: "; cin >> a >> b; cout << "Voce entrou com " << a << " e " << b << endl; // Troca a com b troca(a, b); cout << "Trocados, eles sao " << a << " e " << b << endl; }
Como voc j viu nas notas anteriores, em C++ os argumentos so passados por valor. Uma vez que somente os valores das variveis so passados, no possvel para a
funo troca() alterar os valores de a e b porque troca() no sabe onde na memria estas variveis esto armazenadas. Alm disso, troca() no poderia ser escrito usando a sentena return porque podemos retornar APENAS UM valor (no dois) atravs da sentena return.
Figura 3: Variveis em memria A varivel inteira i est armazenada no endereo 1342. Ela usa dois bytes de memria (quando um objeto usa mais de um byte, seu endereo onde ele comea - neste caso, 1342 e no 1343). A varivel do tipo char c est armazenada no endereo 1346 e usa um byte de memria. O compilador que controla do local de armazenamento destas variveis em memria.
memria escrever. Na verdade, a funo no sabe que os endereos de memria so associados com a e b, mas ela pode modificar o contedo destes endereos. Portanto, passando uma varivel por referncia (ao invs do valor da varivel), habilitamos a funo a alterar o contedo destas variveis na funo chamadora. A definio da funo troca() deve ser alterada, e a lista de parmetros formais deve ter argumentos no do tipo int, mas referncias para int, ou seja, int & . Quando chamamos a funo troca(), ns continuamos passando parmetros reais a e b, mas desta vez, o compilador sabe que o que ser passado para a funo troca() so as referncias a estas variveis, e no seus valores. Dentro da funo troca() no dever haver mudanas. Uma vez que agora os parmetros formais so referncias, o acesso aos objetos deve ser escrito normalmente, mas deve-se ter em mente que qualquer alterao nos valores dos parmetros formais da funo implica em alterar o valor dos argumentos passados para a funo no momento de sua chamada. Assim, a funo troca() capaz de alterar os valores de a e b ``remotamente''. O programa abaixo a verso correta do problema enunciado para a funo troca():
#include <iostream> using namespace std; /* funo troca(px, py) * ao: troca os valores inteiros apontados por px e py * entrada: apontadores px e py * saida: valor de px e py trocados na origem da chamada da funo * suposies: px e py sao apontadores validos * algoritmo: primeiro guarda o primeiro valor em um temporario e * troca */ void troca(int & px, int & py) { int temp; temp = px; px = py; py = temp; } int main() { int a, b; cout << "Entre dois numeros: "; cin >> a >> b; cout << "Voce entrou com " << a << " e " << b << endl; // Troca a com b -- passa argumentos por referencia troca(a, b); cout << "Trocados, eles sao " << a << " e " << b << endl; }
Basicamente, se a funo precisa alterar o valor de uma varivel no ponto de chamada da funo, ento passamos o nome da varivel como parmetro real, e escrevemos a funo indicando os parmetros como sendo de SADA ou PASSADOS POR REFERNCIA.
12 O pr-processador
O pr-processador um programa que faz alguns processamentos simples antes do compilador. Ele executado automaticamente todas as vezes que seu programa compilado, e os comandos a serem executados so dados atravs de diretivas do prprocessador. Estas diretivas so colocadas em linhas que contm somente a diretiva (elas no so cdigo da linguagem C++ , portanto as regras para elas so um pouco diferentes). As linhas que comeam com um # so comandos para o pr-processador. A linha inteira reservada para este comando (nenhum cdigo C++ pode aparecer nesta linha e comandos do pr-processador no podem estar separados em diversas linhas).
Por conveno, nomes introduzidos por um #define so geralmente em letra maiscula (e variveis so em letra minscula, ou uma mistura de letras minsculas e maisculas). Assim, quando voc v um nome em um programa, voc sabe se o nome refere-se a uma varivel ou um nome definido por um #define. Considere o seguinte programa exemplo que usa PI:
3.14159265
cout << "Entre com o raio: "; cin >> raio; cout << "Circunferencia = " << 2.0 * PI * raio << endl; }
Lembre-se que o nome PI no um nome de varivel. Ele um nome que o prprocessador substituir pelo texto especificado pelo #define (mais ou menos da mesma forma que o comando pesquisa-e-substitui do editor de texto). O compilador nunca v ou sabe sobre PI. O compilador v o seguinte cout do programa acima depois do prprocessador ser executado:
cout << "Circunferencia = " << 2.0 * 3.14159265 * raio << endl;
Assim, se todos os arquivos de funes geomtricas puderem enxergar geom.h, eles compartilharo as mesmas definies. para isso que usamos a diretiva #include, para incluir em seu programa, informaes que esto em outro arquivo. Estas diretivas geralmente esto no incio do programa fonte, antes da definio de funes e varveis. Por exemplo, a diretiva
#include "geom.h"
colocada nos arquivos fontes que contm as funes geomtricas far com que todos eles usem o nome simblico PI ao invs de 3.14159265. O fato do nome do arquivo estar em aspas significa que o arquivo geom.h est no mesmo diretrio que os arquivos fontes (ao invs do diretrio onde se encontram as bibliotecas padro de C++ ). A diretiva
#include <iostream>
colocada no incio do programa fonte para incluir informaes (como prottipos de funes) que so necessrios quando cout e cin so chamados dentro do programa. O arquivo entre < > est em algum diretrio padro conhecido pelo pr-processador. Este arquivo iostream comum a todas as implementaes da linguagem C++ e contm
infomaes necessrias para executar operaes de entrada e sada da entrada e sada padro (teclado e monitor).
12.3 Comentrios
De um modo geral, o pr-processador dos compiladores existentes remove todos os comentrios do arquivo fonte antes do programa ser compilado. Portanto, o compilador nunca v realmente os comentrios.
13 Vetores ou Arrays
Considere o seguinte programa. Este programa pede ao usurio notas de 4 estudantes, calcula a mdia e imprime as notas e a mdia.
int main() { int nota0, nota1, nota2, nota3; int media; cout << "Entre cin >> nota0; cout << "Entre cin >> nota1; cout << "Entre cin >> nota2; cout << "Entre cin >> nota3; a nota do estudante 0: "; a nota do estudante 1: "; a nota do estudante 2: "; a nota do estudante 3: ";
media = (nota0 + nota1 + nota2 + nota3) / 4; cout << "Notas: " << nota0 << " " << nota1 << " " << nota2 << " " << nota3 << endl; cout << "Media: " << media << endl; }
Este programa bem simples, mas ele tem um problema. O que acontece se o nmero de estudantes aumentar ? O programa ficaria muito maior (e feio !!). Imagine o mesmo programa se existissem 100 estudantes. O que precisamos uma abstrao de dados para agrupar dados relacionados. Este o objetivo de arrays em C++ . Um array uma coleo de um ou mais objetos, do mesmo tipo, armazenados em endereos adjacentes de memria. Cada objeto chamado de elemento do array. Da mesma forma que para variveis simples, damos um nome ao array. O tamanho do array o seu nmero de elementos. Cada elemento do array numerado, usando um inteiro chamado de ndice. Em C++ , a numerao comea com 0 e aumenta de um em um. Assim, o ltimo ndice igual ao nmero de elementos do array menos um. Por exemplo, podemos definir um array nota de tamanho 100 para armazenar as notas dos cem estudantes:
int nota[100];
Quando o compilador encontra esta definio, ele aloca 200 bytes consecutivos de memria (dois bytes - referente a cada int - para cada nota). Cada nota pode ser acessada dando o nome do array e o ndice entre colchetes: como nota[0] (para a primeira nota), nota[1] para a segunda nota, e assim por diantes, at a ltima nota, nota[99].
O primeiro exemplo um array de 5 inteiros (o tipo int) com o nome total. Como a numerao de arrays comea com 0, os elementos da array so numerados 0, 1, 2, 3 e 4. O segundo exemplo um array de 42 elementos do tipo float com ndices de 0 a 41. Cada elemento do array total do tipo inteiro e pode ser usado do mesmo jeito que qualquer varivel inteira. Para nos referirmos a um elemento do array, usamos colchetes tambm ([ e ]). O valor dentro dos colchetes pode ser qualquer expresso do tipo inteiro. Quando um array definido, armazenamento suficiente (bytes contnuos na memria) so alocados para conter todos os elementos do array. Note na tabela de precedncia abaixo que [ ] tem precedncia maior que todos os demais operadores. Operador Associatividade
esquerda para direita direita para esquerda esquerda para direita esquerda para direita esquerda para direita esquerda para direita esquerda para direita esquerda para direita direita para esquerda
Agora, podermos reescrever o programa que calcula a mdia de uma classe de 4 alunos:
int main() { int indice, nota[4]; float total; indice = 0; while (indice < 4) { cout << "Entre a nota do estudante " << indice << ": "; cin >> nota[indice]; indice = indice + 1; } cout << "Notas: ";
total = 0; indice = 0; while (indice < 4) { cout << nota[indice] << " "; total = total + nota[indice]; indice = indice + 1; } cout << endl << "Media: " << total / 4 << endl; }
Exemplo de Sada:
Entre Entre Entre Entre a a a a nota nota nota nota do do do do estudante estudante estudante estudante 0: 1: 2: 3: 93 85 74 100
O cdigo-fonte do programa consideravelmente mais curto. O nico problema que ainda no fcil modificar o programa para cem alunos porque 4 est em vrios pontos do programa. Ns podemos usar o #define para manter o tamanho do array como uma constante simblica ao invs de utilizar uma constante numrica.
#define ESTUDANTES 4 int main() { int indice, nota[ESTUDANTES]; float total; indice = 0; while (indice < ESTUDANTES) { cout << "Entre a nota do estudante " << indice << ": "; cin >> nota[indice]; indice = indice + 1; } cout << "Notas: ";
total = 0; indice = 0; while (indice < ESTUDANTES) { cout << nota[indice] << " "; total = total + nota[indice]; indice = indice + 1; } cout << endl << "Media: " << total / ESTUDANTES << endl; }
No primeiro exemplo, valor um array de 4 inteiros onde valor[0] e' 1, valor[1] e' 42, valor[2] e' -13, e valor[3] e' 273.
Note que no segundo exemplo, o tamanho do array foi omitido. Neste caso, o compilador calcula o tamanho como sendo o nmero de elementos listados. Quando um array definido, se ele no for inicializado, o tamanho do array deve ser especificado. Se o array for inicializado, o tamanho pode ser omitido. O segundo exemplo acima equivalente a
int peso[3] = { 153, 135, 170 };
Se o tamanho no for omitido, o nmero de elementos presentes no deve exceder o tamanho. Se exceder, o compilador gerar uma mensagem de erro. Se houver menos elementos na lista de inicializao, ento os elementos dados so usados para inicializar os primeiros elementos do array. Qualquer elemento no inicializado conter lixo. Note que este tipo de inicializao s vlido no contexto onde o array definido. Uma sentena como a seguinte produzir um erro do compilador, uma vez que arrays s podem ser inicializados quando definidos.
int erro[5]; erro = { 2, 4, 6, 8, 10 }; // ISTO ESTA' ERRADO
H mais uma restrio na inicializao de um array. Os valores devem ser todos constantes - nenhuma varivel ou expresso permitida. O seguinte trecho de programa produz um erro porque um dos valores de inicializao uma varivel:
int x = 21; int yy[3] = { 1, 2, x }; // ISTO ESTA' ERRADO
antes. MORAL: depois que seu programa compilar com sucesso, salve o seu programa em disco antes de execut-lo. Por exemplo, considere o seguinte programa:
#include <iostream> using namespace std; #define TAM 10 int main() { int ops[TAM], i; // Acesso fora dos limites quando i == TAM i = 0; while (i <= TAM) { ops[i] = 0; i = i + 1; } }
Este programa inicializa cada elemento do array com 0. O problema ocorre quando i tem o valor 10. Neste ponto, o programa coloca 0 em ops[10] . Isto pode produzir resultados indefinidos (e desastrosos) embora o compilador no gere nenhum erro. O problema est na condio do while, que no deveria permitir que o corpo da repetio fosse executado quando i assumisse o valor 10.
i = 1; while (i < TAMANHO) { if (max < a[i]) { max = a[i]; } i = i + 1; } return max; } /* Programa principal */ int main() { int i, valor[TAMANHO]; i = 0; while (i < TAMANHO) { cout << "Entre um inteiro: "; cin >> valor[i]; i = i + 1; } cout << "O maior eh " << array_max(valor) << endl; }
Em main() a chamada para array max() tem valor como seu argumento, que copiado para o parmetro formal a, que um array de inteiros. Note que o tamanho no foi especificado, somente o nome do array, a. Porm tambm correto incluir o tamanho (isto uma questo de estilo - escolha o que voc preferir):
int array_max(int a[TAMANHO]) { ... }
A incluso do tamanho de um array unidimensional na definio da funo somente por razes de legibilidade. At este ponto, parece que no h diferena entre passar uma varivel simples e um array como argumento para uma funo. Mas h uma diferena fundamental: QUANDO DEFINIMOS UM ARRAY COMO ARGUMENTO FORMAL, ALTERAES NO ARRAY FEITAS DENTRO DA FUNO ALTERAM O CONTEDO DO ARRAY PASSADO COMO PARMETRO REAL NA CHAMDA DA FUNO. EM OUTRAS PALAVRAS, QUANDO SO PARMETROS DE FUNES, ARRAYS SO PASSADOS POR
REFERNCIA (sem a necessidade do & na definio do parmetro formal, como foi visto na Seo 11). Para ilustrar este conceito, considere o exemplo seguinte:
#include <iostream> using namespace std; // Troca o valor de uma variavel void troca( int a ){ a = 20; } // Troca valores de elementos em um vetor void troca_vet( int vet[] ){ vet[0] = 60; vet[1] = 70; vet[2] = 80; } // Programa Principal int main() { int x, y; int v[3]; x = 10; v[0] = 30; v[1] = 40; v[2] = 50; troca( x ); cout << "x=" << x << endl; troca_vet( v ); cout << "v[0]=" << v[0] << " v[1]=" << v[1] << " v[2]=" << v[2] ; }
O valor da varivel x do programa principal no se altera porque como j vimos nas notas de aula 7, quando a funo troca chamada, o valor do argumento real x avaliado, que 10, este valor copiado para o parmetro formal a da funo troca e a funo ento executada. O parmetro a da funo tratada como varivel local, portanto quando atribumos 20 a a, estamos atribuindo 20 a uma varivel local. Terminada a funo, a execuo retorna ao programa principal, que imprime o valor de x, que no foi alterado, ou seja, imprime x=10. Quando a funo troca_vet chamada, o array v passado como argumento e ``copiado'' para o parmetro formal vet. A funo ento executada, e os elementos do array so alterados para 60, 70, 80. Como mencionado anteriormente, quando passamos um array como parmetro, as alteraes feitas no array dentro da funo alteram o array passado como parmetro. Portanto, quando a funo termina e a execuo continua no programa principal com a impresso dos valores dos elementos de
Vamos entender por que quando passamos s o nome do array como argumento as alteraes afetam o array passado como parmetro real. Como j mencionamos anteriormente, quando um array definido, como v no programa principal acima, alocado espao suficiente na memria para conter todos os elementos do array. Na ilustrao abaixo, so alocados 6 bytes de memria a partir do endereo 1342 para conter o array. O array como um todo no tem um valor, mas cada elemento do array tem (neste caso, foram inicializados com 30, 40, 50). O nome do array, na verdade, contm o endereo onde comea o array, neste caso, o endereo 1342. Portanto, quando passamos o nome do array como argumento para uma funo estamos na realidade passando como argumento o endereo de memria onde comea o array. No exemplo anterior, 1342 passado como argumento para o parmetro formal vet da funo troca_vet. Portanto, da mesma forma que no caso da varivel simples, o valor de v, que o endereo 1342, copiado para o parmetro vet de troca_vet. Ento, quando a funo troca_vet executada, vet um array de elementos do tipo int que comea no endereo 1342. Quando atribumos o valor 60 a vet[0], estamos atribuindo 60 ao primeiro elemento do array que comea no endereo 1342. Como este o mesmo endereo onde comea o array v do programa principal, quando a funo troca_vet termina, o array v ``enxergar'' o valor dos elementos do array que comea no endereo 1342, que foram alterados pela funo.
Quando passamos variveis simples como argumento para uma funo estamos passando somente o valor da varivel, portanto, de dentro da funo no possvel saber qual o endereo da varivel para poder alter-la. Lembre-se que o endereo s passado para a funo quando passamos o array COMO UM TODO (ou seja, o nome do array, sem ser indexado por um elemento). Se passarmos como argumento apenas um elemento do array, o comportamento o mesmo que se passssemos uma varivel simples. Ou seja, o nome do array indexado por um valor entre colchetes refere-se ao valor do elemento do array, enquanto o nome do array sozinho refere-se ao endereo onde comea o array. Assim, no programa abaixo:
#include <iostream> using namespace std; // Troca o valor de uma variavel void troca( int a ){ a = 20; } // Troca valores de elementos em um vetor void troca_v( int vet[] ){ vet[0] = 60; vet[1] = 70; vet[2] = 80; } // Programa Principal int main(){ int v[] = {30, 40, 50}; troca( v[0] ); cout << "v[0]=" << v[0] << endl; troca_v( v ); cout << "v[0]=" << v[0] << " v[1]=" << v[1] << " v[2]=" << v[2] ; }
A sada do programa :
v[0]=30 v[0]=60 v[1]=70 v[2]=80
Outro exemplo: a funo inicializaArray abaixo inicializa todos os elementos do array valor com um valor passado como argumento pelo programa principal.
#include <iostream> using namespace std; #define TAMANHO 30 // funo inicializaArray(a, k) // ao: inicializa todos os elementos de a com k // entrada: array de inteiros a, inteiro k // saida: nenhum // suposies: a tem TAMANHO elementos // algoritmo: uma repeticao for, inicializando um elemento a // cada repeticao // void inicializaArray(int a[], int k) { int i; i = 0; while (i < TAMANHO) { a[i] = k; i = i + 1; } } // Programa principal int main()
{ int valor[TAMANHO]; // Inicializa todos os elementos do array com 42 inicializaArray(valor, 42); // O resto do programa principal // . // . // . }
Como as alteraes feitas por inicializaArray so vistas do programa principal, depois da funo inicializaArray ser executada, no programa principal todos os elementos do array valor tero o valor 42.
13.5.1 O Problema
Escreva uma funo pesquisa linear que tem como argumento de entrada: um array de inteiros a ser pesquisado, o tamanho do array, e uma chave (um valor inteiro) a ser procurado. A funo retorna um inteiro: o ndice do elemento do array (se a chave for achada) ou -1 caso contrrio. 1. Prottipo:
2. int pesquisa_linear(int [], int, int);
3. Definio:
4. /* Procura uma chave em um array 5. * entrada: array a ser pesquisado (arr ), tamanho do array (tam), 6. * chave a ser procurada (chave) 7. * saida: o indice do elemento que e' igual a chave ou -1 caso nao ache 8. * suposicao: nao assume que o array esteja ordenado 9. */ 10. int pesquisa_linear(int arr[], int tam, int chave) 11. { 12. int i; 13. 14. i = tamanho - 1; 15. while (i >= 0) 16. { 17. if (arr[i] == chave)
18. { 19. return i; 20. } 21. 22. i = i - 1; 23. } 24. return -1; 25. }
1 1 1 1 1 1
2 2 2 2 2 2
3 3 3 3 3 3
7 4 4 4 4 4
5 5 5 5 5 5
8 8 8 6 6 6
9 9 9 9 7 7
4 7 7 7 9 8
6 6 6 8 8 9
Note que mesmo que se comessemos com um array ordenado de 9 elementos, ainda assim o algoritmo dado faz 8 passagens sobre o array.
3. Definicao
4. #include <iostream> 5. 6. using namespace std; 7. 8. #define TAM 9 9. 10. void imprimePassos (int v[], int tam, int passo) 11. { 12. int i; 13. 14. cout << "Passo " << passo << ": --> "; 15. 16. i = 0; 17. while (i < tam) 18. { 19. cout << v[i] << " "; 20. i = i + 1; 21. } 22. cout << endl; 23. } 24. 25. /* Uma funcao que encontra o menor valor em um array entre os indices 26. * "a" e "b" (inclusive) 27. */ 28. int menor_indice(int v[], int a, int b) 29. { 30. int i; 31. int menor; 32. 33. menor = a; 34. i = a+1; 35. 36. while (i <= b) 37. { 38. if ( v[i] < v[menor] ) 39. {
40. 41. 42. 43. 44. 45. 46. 47. 48. 49.
/* Uma funcao que troca os valores entre dois elementos de um array */ 50. void troca_v( int vet[], int i, int j) 51. { 52. int aux; 53. 54. aux = vet[i]; 55. vet[i] = vet[j]; 56. vet[j] = aux; 57. } 58. 59. /* Uma funcao que ordena um array de inteiros usando o algoritmo de 60. * Select sort. 61. * Entrada: array a ser ordenado -- lista[] 62. * tamanho do array -- tam 63. */ 64. void selectSort(int lista[], int tam) 65. { 66. int i,j, 67. idx_menor_elem; /* indice do menor valor no array entre i e tam-1 */ 68. 69. i = 0; 70. while (i < tam) 71. { 72. imprimePassos( lista, tam, i ); 73. 74. idx_menor_elem = menor_indice ( lista, i, tam-1 ); 75. troca_v ( lista, i, idx_menor_elem ); 76. i = i + 1; 77. } 78. } 79. 80. int main () 81. { 82. int vetor[TAM], i; 83. 84. i=0; 85. while (i < TAM) 86. { 87. cin >> vetor[i]; 88. i = i + 1; 89. } 90. 91. selectSort(vetor, TAM); 92. 93. }
O algoritmo abaixo ligeiramente melhor que o anterior e chamado Bubble sort. Ele bastante simples, porm ainda no muito eficiente. Basicamente, o algoritmo funciona da seguinte forma:
na primeira passagem sobre o array: comeando do ltimo elemento do array at o segundo elemento, compare o valor de cada elemento com o valor do elemento anterior a ele. Se os elementos comparados estiverem fora de ordem, trocar os seus valores. Depois que esta primeira passada terminar, o que acontece que o menor elemento do array torna-se o primeiro elemento do array. na segunda passagem pelo array: comeando com o ltimo elemento do array at o terceiro elemento, compare o valor de cada elemento com o valor do elemento anterior a ele. Se os dois elementos comparados estiverem fora de ordem, trocar os seus valores. Depois que esta passagem sobre o array terminar, o segundo menor elemento do array ser o segundo elemento do array. repetir a passagem sobre o array de maneira similar at que a ltima passagem ser simplesmente uma comparao dos valores do ltimo elemento com o elemento anterior.
Por exemplo, se comearmos com um array: 9 5 2 7 3 8 1 4 6, (o primeiro elemento 9 e o ltimo elemento 1) isto o que acontece com os elementos do array depois de cada passagem sobre ele (e troca de valores adjacentes):
passagem ~~~~ 0 --> 1 --> 2 --> 3 --> 4 --> 5 --> 6 --> 7 --> 8 --> conteudo do array depois da passagem ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 5 2 7 3 8 1 4 6 1 1 1 1 1 1 1 1 9 2 2 2 2 2 2 2 5 9 3 3 3 3 3 3 2 5 9 4 4 4 4 4 7 3 5 9 5 5 5 5 3 7 4 5 9 6 6 6 8 4 7 6 6 9 7 7 4 8 6 7 7 7 9 8 6 6 8 8 8 8 8 9
Note que, tambm aqui, mesmo que se comessemos com um array ordenado de 9 elementos, ainda assim o algoritmo dado faz 8 passagens sobre o array. Isto pode ser melhorado da seguinte forma: Antes de comear cada passagem, inicializamos uma varivel ordenado com 1. Se durante a passagem uma troca de valores ocorrer, trocamos o valor da varivel para 0. Assim, se depois da passagem, o valor da varivel continuar sendo 1, isso significa que nenhuma troca ocorreu e que o array est ordenado.
Enquanto o array nao estiver ordenado 1. inicializar ordenado com 1 2. comparar pares adjacentes do array troque seus valores se estiver fora de ordem ordenado = 0.
3. Definicao
4. #include <iostream> 5. 6. using namespace std; 7. 8. #define TAM 9 9. 10. void imprimePassos (int v[], int tam, int passo) 11. { 12. int i; 13. 14. cout << "Passo " << passo << ": --> "; 15. 16. i = 0; 17. while (i < tam) 18. { 19. cout << v[i] << " "; 20. i = i + 1; 21. } 22. cout << endl; 23. } 24. 25. /* Uma funcao que troca os valores entre dois elementos de um array */ 26. void troca_v( int vet[], int i, int j) 27. { 28. int aux; 29. 30. aux = vet[i]; 31. vet[i] = vet[j]; 32. vet[j] = aux; 33. } 34. 35. /* Uma funcao que ordena um array de inteiros usando o algoritmo de 36. * Bubble sort. 37. * Entrada: array a ser ordenado -- lista[] 38. * tamanho do array -- tam 39. */ 40. void bubbleSort(int lista[], int tam) 41. { 42. int ordenado, /* se 1 depois da passagem o array esta' ordenado */ 43. elem_final = 1, /* em uma passagem, elementos do ultimo ate' elem_final 44. sao comparados com o elemento anterior */
i,j, temp;
/* enquanto o array nao estiver ordenado, fazer uma passagem sobre ele */ 49. ordenado = 0; 50. while (ordenado == 0) 51. { 52. ordenado = 1; /* assume que array esta' ordenado */ 53. 54. /* Examina o array do ultimo elemento ate elem_final. Compara 55. cada elemento com o anteior e troca seus valores se estiver 56. fora de ordem. */ 57. 58. imprimePassos(lista, tam, elem_final - 1); 59. 60. i = tam - 1; 61. while (i >= elem_final) 62. { 63. if (lista[i] < lista[i - 1]) /* troca os elementos de i e i-1 */ 64. { 65. troca_v (lista, i, i - 1); 66. ordenado = 0; /* marca como nao ordenado */ 67. } 68. i = i - 1; 69. } 70. 71. elem_final = elem_final + 1; 72. } 73. } 74. 75. int main () 76. { 77. int vetor[TAM], i; 78. 79. i=0; 80. while (i < TAM) 81. { 82. cin >> vetor[i]; 83. i = i + 1; 84. } 85. 86. bubbleSort(vetor, TAM); 87. 88. }
int arr1[4] = {10, 20, 30, 40}; int arr2[4]; arr2 = arr1; // ERRADO: NO copia arr1 em arr2 // tem que copiar elemento por elemento
if( arr1 == arr2 ) // ERRADO: NO podemos comparar arrays inteiros cout << "X"; // tem que comparar elemento por elemento }
Define um array bidimensional chamado tabela que uma matriz 3 por 5 de valores do tipo int (15 valores no total). Os ndices da primeira dimenso vo de 0 a 2, e os ndices da segunda dimenso vo de 0 a 4. Abaixo apresentamos um programa que imprime os elementos de um array bidimensional.
#include <iostream> using namespace std; #define ALTURA 5 #define LARGURA 5 int main() { int x; int y; char matriz [ALTURA] [LARGURA]; num_cols] // preenche a matriz com zeros
// // //
cout << endl << "Entre coordenadas na forma \"y x\"." << endl; cout << "Use valores negativos para sair do programa." << endl; cout << "Coordenadas: "; cin >> y >> x; while (x >= 0 && y >= 0) { matriz[y][x] = 1;
//
y = 0; while (y < ALTURA) // imprime o array todo { x = 0; while (x < LARGURA) { cout << matriz[y][x] << " "; x = x + 1; } cout << endl << endl; y = y + 1; } cout << endl; cout << "Coordenadas: "; cin >> y >> x; } }
Neste exemplo, matriz um array bidimensional. Ela tem nmero de elementos igual a ALTURAxLARGURA, sendo cada elemento do tipo int. O exemplo abaixo preenche os elementos de um array bidimensional com os valores que representam a taboada e imprime a matriz.
// Exemplo de array 2-D - taboada
#include <iostream> #include <iomanip> using namespace std; #define LIN 10 #define COL 10 int main() { int x; int y; int tabela[LIN] [COL]; // preenche a tabela
// // //
x = 0; while (x < COL) { tabela[y][x] = y*x; x = x + 1; } y = y + 1; } cout << endl << " // Tabela de Multiplicacao" << endl;
cout << setw(6) << 0; x = 1; while (x < COL) { cout << setw(3) << x; x = x + 1; } cout << endl; // Imprime uma linha horizontal cout << " "; x = 0; while (x < 3*COL) { cout << "-"; x = x + 1; } cout << endl; // // Imprime as linhas da tablea. Cada linha a precedida pelo indice de linha e uma barra vertical
y = 0; while (y < LIN) { cout << setw(2) << y << "|"; x = 0; while (x < COL) { cout << setw(3) << tabela[y][x]; x = x + 1; } cout << endl; y = y + 1; } }
A sada do programa :
Tabela de Multiplicacao 0 1 2 3 4 5 6 7 8 9 -----------------------------0| 0 0 0 0 0 0 0 0 0 0 1| 0 1 2 3 4 5 6 7 8 9 2| 0 2 4 6 8 10 12 14 16 18 3| 0 3 6 9 12 15 18 21 24 27
4| 5| 6| 7| 8| 9|
0 0 0 0 0 0
4 5 6 7 8 9
8 10 12 14 16 18
12 15 18 21 24 27
16 20 24 28 32 36
20 25 30 35 40 45
24 30 36 42 48 54
28 35 42 49 56 63
32 40 48 56 64 72
36 45 54 63 72 81
14.1 Inicializao
Arrays multidimensionais podem ser inicializados usando listas aninhadas de elementos entre chaves. Por exemplo, um array bidimensional tabela com trs linhas e duas colunas pode ser inicializado da seguinte forma:
double tabela[3][2] = { {1.0, 0.0}, {-7.8, 1.3}, {6.5, 0.0} }; // // // linha 0 linha 1 linha 2
Quando o array inicializado, o tamanho da primeira dimenso pode ser omitido. A definio de array abaixo equivalente a dada anteriormente.
double tabela[][2] = { {1.0, 0.0}, {-7.8, 1.3}, {6.5, 0.0} }; // // // linha 0 linha 1 linha 2
consistindo de um total de .
Arrays multidimensionais so armazenados de forma que o ltimo subscrito varia mais rapidamente. Por exemplo, os elementos do array
int tabela[2][3];
Um array de dimenso k, onde o nmero de elementos em cada dimenso , respectivamente, pode ser imaginado como um array de dimenso cujos elementos so arrays de dimenso .
pode ser imaginado como um array unidimensional de 4 elementos do tipo int[], ou seja, arrays de int; cada um dos 4 elementos um array de 5 elementos do tipo int:
tabela[0] tabela[1] tabela[2] tabela[3] ---> ---> ---> ---> {13, {20, {31, {40, 15, 22, 33, 42, 17, 24, 35, 44, 19, 26, 37, 46, 21} 28} 39} 48}
Quando uma funo com um parmetro formal do tipo array chamada, na chamada da funo somente o nome do array passado como parmetro real. O tipo (e portanto a dimenso) do array passado como parmetro real deve ser consistente com o tipo (e portanto a dimenso) do array que o parmetro formal. O programa abaixo mostra o exemplo da tabela de multiplicao escrita usando funes.
// Exemplo de array 2-D - tabela de multiplicacao #include <iostream> #include <iomanip> using namespace std; #define LIN 10 #define COL 10 // Inicializa o array com a tabela de multiplicacao
y=0; while (y < LIN) { x=0; while(x < COL) { arr[y][x] = y*x; x = x + 1; } y = y + 1; } }
//
// //
cout << setw(6) << 0; x = 1; while (x < COL) { cout << setw(3) << x; x = x + 1; } cout << endl; // imprime uma linha horizontal cout << " "; x = 0; while (x < 3*COL) { cout << "-"; x = x + 1; } cout << endl; // // imprime as linhas do array. cada linha e' precedida pelo numero da linha e uma barra vertical
y = 0; while (y < LIN) { cout << setw(2) << y << "|"; x = 0; while (x < COL) { cout << setw(3) << arr[y][x]; x = x + 1; } cout << endl; y = y + 1; } } int main() { int tabela[LIN] [COL]; inicializa_arr(tabela); cout << endl << " imprime_arr(tabela); } Tabela de Multiplicacao" << endl;
//
#include <iostream> using namespace std; #define ALTURA 7 #define LARGURA 7 // *** DEFINICAO DE FUNCOES ******* // funcao que imprime um array 2-D ALTURA X LARGURA void imprime_matriz(char matriz[][LARGURA]) { int x,y; y = 0; while (y < ALTURA) { x = 0; while (x < LARGURA) { cout << matriz[y][x] << " "; x = x + 1; } cout << endl << endl; y = y + 1; } cout << endl; } // funcao que preenche uma matriz ALTURA X LARGURA com pontos void pontos( char matriz[][LARGURA]) { int x,y; y = 0; while (y < ALTURA) { x = 0; while (x<LARGURA) { matriz[y][x] = '.'; x = x + 1; } y = y + 1; } } /* funcao que preenche os elementos selecionados da matriz com um * quadrado e imprime a matriz */ void seleciona_elem(char matriz[][LARGURA]) { int x, y; cout << endl << "Entre com as coordenadas na forma \"y x\"." << endl; cout << "Use numeros negativos para terminar." << endl;
cout << "Coordenadas: "; cin >> y >> x; while (x >= 0 && y >= 0) { matriz[y][x]='@'; // preenche o elemento com quadrado imprime_matriz(matriz); // imprime a matriz cout << "Coordenadas: "; cin >> y >> x; } } /* funcao que marca todos os elementos abaixo da diagonal principal de * um array ALTURA X LARGURA com quadrados */ void marca_triang(char matriz[][LARGURA]) { int x, y; cout << "Triangulo" << endl; pontos(matriz); y = 0; while (y < ALTURA) { x = 0; while (x <= y) { matriz[y][x] = '@'; x = x + 1; } y = y + 1; } } // funcao que rotaciona ('flip') cada linha array tendo da // diagonal principal como centro da rotao void flip(char matriz[][LARGURA]) { int x, y; int temp; cout << "Flip ao longo da diagonal principal." << endl; y = 0; while (y < ALTURA) { x = 0; while (x <= y) { temp = matriz[y][x]; matriz[y][x] = matriz[x][y]; matriz[x][y] = temp; x = x + 1; } y = y + 1; } } // funcao que espera ate que uma tecla qualquer seja digitada void pausar() { o elemento
char c; cin.get(c); } // ********* MAIN *********** // alguns exemplos de chamadas de funcoes com argumentos array 2-D int main() { char matriz [ALTURA] [LARGURA]; pontos(matriz); seleciona_elem(matriz); pausar(); flip(matriz); imprime_matriz(matriz); pausar(); marca_triang( matriz); imprime_matriz( matriz); pausar(); flip( matriz); imprime_matriz(matriz); pausar(); }
Notas de rodap ... funes1 Na verdade, um programa C++ composto pela definio de funes e de elementos estruturais denominados classes. Estes so tema de estudo em cursos avanados de programao orientada a objetos. 2 ... cin cout e cin so na verdade objetos das classes ostream e istream. Mas este detalhe no abordado nestas notas de aula. Ser visto apenas o uso destes objetos como primitivas simples para Entrada e Sada de dados. ... lgicos3 Operadores lgicos && e || sero vistos na prxima aula.