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:
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 m, n; .... if(m < n) { int auxiliar = m; m = n; n = auxiliar; }
Note-se que, sendo as instruções de selecção instruções por si só, o teste acima se pode escrever simplesmente: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; }
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;
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.if(m == 0) { if(n == 0) cout << "m e n são zero." << endl; } else cout << "m não é zero." << endl;
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.
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 é: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"; }
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:// 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"; }
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:
uma chamada escreveDígitoPorExtenso(7) resulta em: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"; } }
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:
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 < 10) cout << "unidades"; else if(x < 100) cout << "dezenas"; else if(x < 1000) cout << "centenas"; else cout << "milhares"; }
// 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:
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.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, };
* Na realidade os enumerados podem conter valores que não correspondem aos especificados na sua definição. Ver Stroustrup, página 77.
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.DiaDaSemana operator ++ (DiaDaSemana& dia) { if(dia == último) return dia = primeiro; else return dia = DiaDeSemana(dia + 1); }
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?