As quatro linhas, 1 a 4, correspondem a uma única instrução, dita de selecção, que é composta de uma condição (na linha 1) e duas instruções alternativas (linhas 2 e 4). Se x fôr menor do que zero, então é executada a instrução 2, passando o valor de x a ser zero, senão é executada a instrução 4, passando a variável x a ter o valor 1. Em qualquer dos casos, a execução continua na linha 5 (excepto em alguns casos *).if(x < 0) // 1 x = 0; // 2 else // 3 x = 1; // 4 .... // 5
Em certos casos pretende-se apenas executar instruções se determinada condição se verificar e não se desejando fazer nada caso a condição seja falsa. Nestes casos pode-se omitir o else. Por exemplo:
Se x fôr menor do que zero, então é executada a instrução 2, passando o valor de x a ser zero, sendo em seguida executada a instrução na linha 5 (excepto em alguns casos *). No caso contrário a execução passa directamente para a linha 5.if(x < 0) // 1 x = 0; // 2 .... // 3
Em muitos casos é necessário que o computador execute condicionalmente, não uma instrução simples, mas um conjunto de instruções. Para o conseguir, agrupam-se essas instruções num bloco ou instrução composta, colocando-as entre chavetas {}. Por exemplo:
int m, n;
....
if(m < n) {
int auxiliar = m;
m = n;
n = auxiliar;
}
Caso seja necessário podem-se encadear instruções
de selecção umas dentro das outras. Por exemplo, para
verificar qual a posição de a relativamente a um
intervalo [minimo, maximo] :
int a, minimo, maximo;
cin >> minimo >> maximo >> a;
if(a < minimo)
cout << a << " menor que mínimo." << endl;
else {
if(a < maximo)
cout << a << " entre mínimo e máximo." << endl;
else
cout << a << " maior que máximo." << endl;
}
Note-se que, sendo as instruções de selecção
instruções por si só, o teste acima se pode escrever
simplesmente:
No entanto, a sintaxe das instruções if e if else do C++ presta-se a ambiguidades. No código:if(a < minimo) cout << a << " menor que mínimo." << endl; else if(a < maximo) cout << a << " entre mínimo e máximo." << endl; else cout << a << " maior que máximo." << endl;
O else não pertence ao primeiro if, ao contrário do que a indentação do código sugere: pertence ao segundo if. Em caso de dúvida, um else pertence ao if mais próximo (e acima...) dentro mesmo bloco de instruções. Para corrigir o exemplo anterior é necessário construir uma instrução composta, muito embora consista de uma única instrução de selecção:if(m == 0) if(n == 0) cout << "m e n são zero." << endl; else cout << "m não é zero." << endl;
if(m == 0) {
if(n == 0)
cout << "m e n são zero." << endl;
} else
cout << "m não é zero." << endl;
Para evitar erros deste tipo, é conveniente usar blocos de instruções
duma forma liberal, pois construções como a que apresentou
podem dar origem a erros muito difíceis de detectar e corrigir.
Os compiladores de qualidade, no entanto, avisam o programador da presença
de semelhantes ambiguidades.
Um outro erro frequente é escrever:
Neste caso a intenção seria que, depois do if, x conteria o módulo do valor original. Mas a interpretação feita pelo compilador (e a correcta dada a sintaxe da linguagem C++) é:if(x < 0); x = -x;
Ou seja, se x fosse inicialmente positivo tornar-se-ia negativo! Estes erros, não sendo muito comuns, são ainda mais difíceis de detectar.if(x < 0) ; // instrução nula: não faz nada. x = -x;
* Chama-se fluxo do programa à
sequência pela qual as instruções são executadas.
* Uma forma alternativa de
comentários em C++ usa as construções /*
e */. Tudo o que se encontra entre /* e o */
seguinte é interpretado pelo compilador simplesmente como um espaço.
* Podem existir instruções
return, break, continue ou goto que
alterem a linha normal de execução.
void escreveDígitoPorExtenso(int dígito)
{
if(dígito == 0)
cout << "zero";
if(dígito == 1)
cout << "um";
if(dígito == 2)
cout << "dois";
if(dígito == 3)
cout << "três";
if(dígito == 4)
cout << "quatro";
if(dígito == 5)
cout << "cinco";
if(dígito == 6)
cout << "seis";
if(dígito == 7)
cout << "sete";
if(dígito == 8)
cout << "oito";
if(dígito == 9)
cout << "nove";
if(dígito < 0 || dígito > 9)
cout << "erro";
}
Uma vez que dígito não pode tomar dois valores ao
mesmo tempo, esta solução é ineficiente, pois obriga
a onze testes qualquer que seja o dígito a imprimir. Uma alternativa
melhor é:
// Repare na estrutura, em que else e if são colocados na mesma linha
// e se repete a indentação em cada linha. Fica mais claro que é uma
// sequência de alternativas.
void escreveDígitoPorExtenso(int dígito)
{
if(dígito == 0)
cout << "zero";
else if(dígito == 1)
cout << "um";
else if(dígito == 2)
cout << "dois";
else if(dígito == 3)
cout << "três";
else if(dígito == 4)
cout << "quatro";
else if(dígito == 5)
cout << "cinco";
else if(dígito == 6)
cout << "seis";
else if(dígito == 7)
cout << "sete";
else if(dígito == 8)
cout << "oito";
else if(dígito == 9)
cout << "nove";
else
cout << "erro";
}
Existe uma solução melhor para este problema: usando a instrução
de selecção switch. Quando é necessário
comparar uma variável com vários valores possíveis,
e executar uma acção diferente em cada um dos casos, deve-se
usar esta instrução. O procedimento anterior ficaria:
void escreveDígitoPorExtenso(int dígito)
{
switch(dígito) {
case 0: cout << "zero";
break;
case 1: cout << "um";
break;
case 2: cout << "dois";
break;
case 3: cout << "três";
break;
case 4: cout << "quatro";
break;
case 5: cout << "cinco";
break;
case 6: cout << "seis";
break;
case 7: cout << "sete";
break;
case 8: cout << "oito";
break;
case 9: cout << "nove";
break;
default: cout << "erro";
break;
}
}
Esta instrução tem algumas particularidades. Em primeiro
lugar não permite a especificação de gamas de valores
nem de desigualdades: construções como É possível agrupar várias alternativas:
switch(valor) {
case 1:
case 2:
case 3: cout "1, 2, ou 3";
break;
....
}
Isto acontece porque a construção case n:
apenas indica qual o ponto de entrada nas instruções que
compõem o switch quando a sua expressão de controlo
(entre parênteses após a palavra-chave switch) tem
valor n. A execução do corpo do switch
(o bloco de instruções entre {}) só termina
quando for atingida chaveta final, ou quando for executada uma instrução
de break (terminado o switch a execução continua
sequencialmente). A consequência deste facto é que,
se no exemplo anterior se eliminarem os break:
void escreveDígitoPorExtenso(int dígito)
{
switch(dígito) {
case 0: cout << "zero";
case 1: cout << "um";
case 2: cout << "dois";
case 3: cout << "três";
case 4: cout << "quatro";
case 5: cout << "cinco";
case 6: cout << "seis";
case 7: cout << "sete";
case 8: cout << "oito";
case 9: cout << "nove";
default: cout << "erro";
}
}
uma chamada escreveDígitoPorExtenso(7) resulta em:
que não é o que se pretendia!seteoitonoveerro
Note-se que, pelas razões que se indicaram atrás, não é possível usar a instrução switch (pelo menos duma forma imediata) como alternativa a:
// Pré-condição: 0 <= valor e valor < 10000.
void escreveGama(double x)
{
if(x < 10)
cout << "unidades";
else if(x < 100)
cout << "dezenas";
else if(x < 1000)
cout << "centenas";
else
cout << "milhares";
}
Suponha que os valores passados como argumento a escreveGama()
são equiprováveis. Nesse caso consegue-se demostrar
que, em média, são necessárias 2,989 comparações
ao executar o procedimento e que, invertendo para
// Pré-condição: 0 <= valor e valor < 10000.
void escreveGama(double x)
{
if(x >= 1000)
cout << "milhares";
else if(x >= 100)
cout << "centenas";
else if(x >= 10)
cout << "dezenas";
else
cout << "unidades";
}
são necessárias 1,11 comparações (demonstre-o)!
A ordem pela qual se fazem as comparações pode ser muito
relevante!
enum DiaDaSemana {
segunda_feira,
terça_feira,
quarta_feira,
quinta_feira,
sexta_feira,
sábado,
domingo
};
define um tipo enumerado com sete valores possíveis, um para cada
dia da semana *. O novo tipo utiliza-se como
habitualmente em C++:
Pode-se atribuir atribuir a esta variável qualquer dos valores listados na definição:DiaDaSemana dia;
Cada um dos valores associados ao tipo DiaDaSemana (viz. segunda_feira, ..., domingo) é utilizado no código como um valor literal para esse tipo, tal como 10 é um valor literal do tipo int ou 'a' é uma valor literal do tipo char. Como se trata de um tipo definido pelo utilizador, não é possível, sem mais esforço, ler valores desse tipo do teclado ou escrevê-los no ecrã usando os métodos habituais (viz. cin >> e cout <<). Mais tarde ver-se-á como se pode "ensinar" o computador a ler e escrever tipos enumerados de dados.dia = terça_feira;
Na maioria dos casos os tipos enumerados são usados para tornar
mais claro o significado dos valores atribuidos a uma
variável, pois segunda_feira tem claramente mais significado
que 0! Mas, na realidade, os valores de tipos enumerados
são representados como inteiros atribuidos sucessivamente a partir
de zero. Assim, segunda_feira tem representação
interna 0, terça_feira tem representação
1, etc. De facto, se se tentar imprimir segunda_feira o
resultado será 0, que é a sua representação
na forma de um inteiro. É possível atribuir inteiros
arbitrários a cada um dos valores duma enumeração,
pelo que podem existir representações idênticas para
valores com nome diferentes:
enum DiaDaSemana { // agora com nomes alternativos
primeiro = 0, // desnecessário
segunda = primeiro,
segunda_feira = segunda,
terça,
terça_feira = terça,
quarta,
quarta_feira = quarta,
quinta,
quinta_feira = quinta,
sexta,
sexta_feira = sexta,
sábado,
domingo,
último = domingo,
};
Se um operando de um tipo enumerado ocorrer numa expressão, o C++
geralmente convertê-lo-á num inteiro. Essa conversão
também se pode especificar directamente, escrevendo int(segunda_feira),
por exemplo. As conversões opostas também são
possíveis, usando DiaDaSemana(2), por exemplo, para obter
quarta_feira. Na próxima secção ver-se-á
como redefinir os operadores do C++ para operarem sobre tipos enumerados
sem surpresas desagradáveis para o programador/utilizador.
* Na realidade os enumerados podem conter valores que não correspondem aos especificados na sua definição. Ver Stroustrup, página 77.
DiaDaSemana operator ++ (DiaDaSemana& dia)
{
if(dia == último)
return dia = primeiro;
else
return dia = DiaDeSemana(dia + 1);
}
Assim, a incrementação de uma variável do tipo DiaDeSemana
conduz sempre ao dia da semana subsequente. Note-se que se utilizou
primeiro e último e não segunda_feira
e domingo, pois dessa forma pode-se mais tarde decidir que a semana
começa ao domingo sem ter de alterar o procedimento acima, alterando
apena a definição da enumeração.
2. Escreva um programa que, dado um número inteiro entre 0 e 99 o escreva por extenso. Exemplo:
Se se desejasse que o programa escrevesseIntroduza um número: 35 O número introduzido foi três dezenas e cinco unidades.
o que se deveria fazer?O número introduzido foi trinta e cinco.
3. Escreva uma função que devolva o número de dias de um mês num dado ano passado como argumento. Lembre-se que os anos múltiplos de 4 são bissextos, com excepção dos múltiplos de 100, mas incluindo os múltiplos de 400 (esta fórmula só é válida para datas ocidentais em geral depois de 1752).
4.a) Faça um programa que, dado um número inteiro que representa a idade de uma pessoa, escreva qual a sua faixa etária. As faixas etárias são definidas do seguinte modo: entre 0 e 12 anos - criança, de 12 a 20 - adolescente, de 20 a 60 - adulto, acima de 60 - idoso.
4.b) Modifique o programa anterior de modo a que seja inserido o sexo da pessoa (m ou f) em questão e as respostas sejam adequadas a esta informação. Por exemplo, se a pessoa em questão for do sexo feminino em vez de escrever "adulto" deve escrever "adulta".
2. Caso já se sinta apto a usar a noção de ciclo, tente fazer um programa que descodifique toda a cadeia de zeros e uns. Atenção: pode haver sequências que não terminam com a codificação total de um caracter (por exemplo 01 = A?). O seu programa deve estar preparado para lidar com esse tipo de erros do utilizador. Exemplo:Introduza um conjunto de zeros e uns: 1001001100110 C
Introduza um conjunto de zeros e uns: 10010011001101 CCBAABA?