Execução condicional com if
A maioria dos programas requer que alguns comandos apenas sejam executados em algumas condições e isso é chamado, naturalmente, de execução condicional.
A principal estrutura na linguagem C para determinar se determinados comandos são ou não executados é o if
. Esta estrutura é o assunto deste capítulo.
A estrutura condicional if
Para introduzir a estrutura condicional é apresentado um programa que implementa o Algoritmo 1.
Algoritmo 1: Apresentação de dois valores reais em ordem não decrescente.
A codificação em C deste algoritmo pode ser feita como se segue.
/*
Apresentação de dois valores em ordem não decrescente
Requer: dois valores reais v1 e v2
Assegura: v1 <= v2
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite dois valores reais: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble v1, v2;
(entrada, "%lf%lf", &v1, &v2);
sscanf
if(v2 < v1) {
double temporario = v1;
= v2;
v1 = temporario;
v2 }
("Valores em ordem não decrescente: %g e %g.\n", v1, v2);
printf
return 0;
}
4.5 1.3
Digite dois valores reais: Valores em ordem não decrescente: 1.3 e 4.5.
A estratégia da implementação é a mesma do algoritmo, que opta por fazer a troca dos valores (e isso requer o uso de uma variável temporária auxiliar). As três instruções que realizam a troca somente são executadas quando v2
for menor que v1
e ignoradas se isso não for verdade
É importante ressaltar que as chaves agrupam as três atribuições do if
, de forma que todos os comandos ficam condicionados. O uso das chaves para agrupar comandos cria um comando composto, o qual pode ser usado no lugar de qualquer comando simples.
Em C, a estrutura básica do if
se estabelece conforme destacado na sequência.
A condição é uma expressão que deve resultar em verdadeiro ou falso. Somente se a condição for verdadeira comando é executado. O comando, por sua vez, pode ser tanto um comando simples (terminado com ponto e vírgula) ou um comando composto (usando chaves).
Seguem dois exemplos válidos de estruturas if
: a primeira usa um comando simples e, na segunda, um comando composto contendo apenas um comando simples. É interessante observar que o ponto e vírgula ocorre apenas no final do comando simples, enquanto comandos compostos não usam esse terminador depois do fecha chaves.
if(valor < 0)
= -valor; // torna positivo valor
if(valor < 0) {
= -valor; // torna positivo
valor }
if
+ else
A cláusula else
pode ser adicionada ao if
para indicar uma ação a ser feita caso a condição de teste seja avaliada como falsa.
Como exemplo, considere o problema de determinar o peso (massa) ideal de uma pessoa baseada em seu sexo biológico. Para o sexo feminino, o peso em quilogramas é dado pela expressão 62,1\(h\) - 44,7, sendo \(h\) a altura da pessoa em metros; para o masculino, o cálculo é 72,7\(h\) - 58. Seguem a solução algorítmica e uma implementação em C para esse problema.
Algoritmo 2: Estimativa do peso ideal conforme sexo biológico e altura.
/*
Cálculo estimado da massa ideal de uma pessoa baseada em seu sexo biológico
e sua altura
Requer: o sexo biológico (masculino ou feminino) e a altura em metros
Assegura: a massa ideal da pessoa apresentada (kg)
*/
#include <stdio.h>
int main(void) {
char entrada[160];
("Digite o sexo (M ou F) e a altura em quilogramas: ");
printf(entrada, sizeof entrada, stdin);
fgetschar sexo;
double altura;
(entrada, "%c%lf", &sexo, &altura);
sscanf
double massa_ideal;
if(sexo == 'F')
= 62.1 * altura - 44.7; // feminino
massa_ideal else
= 72.7 * altura - 58; // masculino
massa_ideal
("Massa ideal: %.1fkg.", massa_ideal);
printf
return 0;
}
F 1.68
Digite o sexo (M ou F) e a altura em quilogramas: Massa ideal: 59.6kg.
Neste há dois caminhos possíveis: o cálculo como sexo biológico feminino ou masculino. Pela especificação do problema, apenas M
ou F
são entradas válidas, o que permitiu associar ao else
, inequivocamente, o valor M
. Em C, vale o destaque que =
é usado para a atribuição, sendo que a comparação de igualdade é feita com ==
.
Em relação ao if
, tanto no caso verdadeiro quanto no falso foram inseridos comandos simples.
Como exemplo adicional, um algoritmo para classificar um triângulo como equilátero, escaleno ou isósceles a partir do comprimento de seus lados é apresentado no Algoritmo 3.
Algoritmo 3: Classificação de um triângulo em relação aos comprimentos de seus lados
Em C, a codificação pode ser expressa na forma seguinte.
#include <stdio.h>
/*
Classificação de um triângulo em relação aos comprimentos de seus lados
Requer: Os comprimentos dos lados de um triângulo válido
Assegura: uma classificação em equilátero, escaleno ou isósceles
*/
int main(void) {
char entrada[160];
("Digites os comprimentos dos lados de um triângulo: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble lado1, lado2, lado3;
(entrada, "%lf%lf%lf", &lado1, &lado2, &lado3);
sscanf
char *classificacao;
if(lado1 == lado2 && lado2 == lado3)
= "equilátero";
classificacao else if(lado1 == lado2 || lado2 == lado3 || lado1 == lado3)
= "escaleno";
classificacao else
= "isósceles";
classificacao
("O triângulo é %s.\n", classificacao);
printf
return 0;
}
9.1 4.3 9.1
Digites os comprimentos dos lados de um triângulo: O triângulo é escaleno.
O programa usa comandos simples para as atribuições, o que dispensa os comandos compostos com as chaves. Na lógica da solução inicialmente verificada a hipótese dos três lados iguais (equilátero). Caso essa verificação dê falso, então há pelo menos um lado diferente e isso leva ao segundo if
, que verifica a hipótese de haver algum par de lados de mesmo comprimento.
Este programa, além do if
e algumas operações lógicas com e e ou, também usa uma variável classificacao
declarada como do tipo char *
. Quando são feitas atribuições como as usadas no exemplo a esse tipo de variável, é feita apenas uma referência à constante e não uma atribuição convencional, na qual uma cópia do texto é guardada na variável. A manipulação de cadeias de caracteres em C é, para se bem dizer, chata e difícil. Como ela não é tão natural, acaba exigindo conhecimento de outros elementos da linguagem. O ?@sec-dados-textuais aborda esse tema com mais detalhes.
Nas comparações com valores double
resultantes de expressões aritméticas, nunca se deve verificar pela igualdade. Os valores reais são armazenados com precisão limitada. É conveniente o uso uma tolerância nessas comparações.
/*
Ilustração do problema de precisão nas operações com double
*/
#include <stdio.h>
#include <math.h>
int main(void) {
double a = 40.96;
double b = 6.4 * 6.4; // 40.96
// Opção ruim
if(a == b)
("%g == %g.\n", a, b);
printfelse {
("%g != %g. :-(\n", a, b);
printf("Diferença: %g.\n\n", a - b);
printf}
// Opção melhor, contornando o problema
if(fabs(a - b) < 1e-5) // cinco casas decimais...
("%g == %g. (o suficiente!)\n", a, b);
printfelse {
("%g != %g. :-(\n", a, b);
printf("Diferença: %g.\n\n", a - b);
printf}
return 0;
}
40.96 != 40.96. :-(
Diferença: -7.10543e-15.
40.96 == 40.96. (o suficiente!)
Neste programa, se a diferença for menor que 0,00001 (segundo if
), então os valores são considerados iguais. A tolerância, é claro, depende do contexto do problema.
Como um último exemplo, segue o algoritmo para o cálculo das raízes reais de uma equação de segundo grau \({ax^2 + bx + c = 0}\) dados seus coeficientes (Algoritmo 1, apresentado no ?@sec-nocoes-de-algoritmos).
Sua codificação em C pode ser apresentada conforme se segue.
/*
Cálculo e apresentação das raízes reais de uma equação de segundo grau na
forma ax^2 + bx + c = 0
Requer: Os coeficientes a, b e c da equação
Assegura: as raízes reais da equação; ou mensagem que a equação é
inválida; ou mensagem que não há raízes reais
*/
#include <stdio.h>
#include <math.h>
int main(void) {
char entrada[160];
("Digite os valores de a, b e c da equação: ");
printf(entrada, sizeof entrada, stdin);
fgetsdouble a, b, c;
(entrada, "%lf%lf%lf", &a, &b, &c);
sscanf
if(a == 0)
("Não é equação do segundo grau (a = 0).\n");
printfelse {
double discriminante = pow(b, 2) - 4 * a * c;
if(discriminante < 0)
("A equação não possui raízes reais.\n");
printfelse if(discriminante < 1.0e-10) { // tolerância: < 1.0e-10 é "zero"
// Uma raiz real
double x = -b / (2 * a);
("Uma raiz: %g.\n", x);
printf}
else {
// Duas raízes reais
double x1 = (-b - sqrt(discriminante)) / (2 * a);
double x2 = (-b + sqrt(discriminante)) / (2 * a);
("Raízes reais: %g e %g.\n", x1, x2);
printf}
}
return 0;
}
1 -5.9 7.14
Digite os valores de a, b e c da equação: Raízes reais: 1.7 e 4.2.
De quem é esse else
?
Um ponto relevante quando se usa if
dentro de if
é a quem pertence um determinado else
. Para considerar esse problema é apresentado um código em C.
/*
Ilustração do problema do else pendente
*/
#include <stdio.h>
int main(void) {
int i = 0;
int j = 0;
int k = 5;
if(k > 10)
if (k > 20)
= 10;
i else
= 10;
j
("i = %d e j = %d.\n", i, j);
printf
return 0;
}
i = 0 e j = 0.
A intenção do código seria fazer j = 10
para valores de k
menores ou iguais a 10. Porém, apesar da organização visual sugerir que o else pertence ao if(k > 10)
, ele se liga ao if(k > 20)
. Assim, independentemente da disposição dos comandos no codigo fonte, o programa será sempre interpretado como se segue. E, neste caso, o primeiro if
não possui else
.
/*
Ilustração do problema do else pendente
*/
#include <stdio.h>
int main(void) {
int i = 0;
int j = 0;
int k = 5;
if(k > 10)
if (k > 20)
= 10;
i else
= 10;
j
("i = %d e j = %d.\n", i, j);
printf
return 0;
}
O else
sempre se ligará ao if
mais próximo da sequência das instruções. Para resolver esse problema do exemplo, é preciso isolar o if
mais interno, colocando-o em um bloco, formando um comando composto.
O resultado é apresentado na sequência.
/*
Ilustração do problema do else pendente
*/
#include <stdio.h>
int main(void) {
int i = 0;
int j = 0;
int k = 5;
if(k > 10) {
if (k > 20)
= 10;
i }
else
= 10;
j
("i = %d e j = %d.\n", i, j);
printf
return 0;
}
i = 0 e j = 10.
É ainda interessante notar que, caso o if(k > 20)
tivesse seu próprio else
, a ambiguidade desapareceria e o comando composto seria desnecessário.
/*
Ilustração do problema do else pendente
*/
#include <stdio.h>
int main(void) {
int i = 0;
int j = 0;
int k = 15;
if(k > 10)
if (k > 20)
= 10;
i else
= 20;
i else
= 10;
j
("i = %d e j = %d.\n", i, j);
printf
return 0;
}
i = 20 e j = 0.
A manutenção da organização visual do código com as indentações corretas ajuda na identificação dos erros.
Boas práticas
- Evitação de redundância nas condições.
- Escrita de código com legibilidade.
- Evitação de comparações desnecessárias.
- Cuidado com a qual
if
um dadoelse
pertence. - Emprego coerente de declarações locais a comandos compostos (blocos).
- Evitação de “sombreamento” de identificadores (repetição de identificador em escopos diferentes muito próximos).